Merge branch 'uniapp-vue3' of https://gitee.com/beijing_hongye_huicheng/lilishop-uniapp into uniapp-vue3

This commit is contained in:
pikachu1995@126.com
2026-07-13 17:09:40 +08:00
189 changed files with 19104 additions and 21267 deletions

404
App.vue
View File

@@ -1,256 +1,200 @@
<script>
/**
* vuex管理登录状态具体可以参考官方登录模板示例
*/
import config from "@/config/config";
<script setup lang="ts">
import config from '@/config/config'
import { getClipboardData } from '@/js_sdk/h5-copy/h5-copy.js'
import APPUpdate from '@/plugins/APPUpdate'
import storage from '@/utils/storage'
import { getThemeSetting } from '@/api/common.js'
import {
getClipboardData
} from "@/js_sdk/h5-copy/h5-copy.js";
import APPUpdate from "@/plugins/APPUpdate";
import storage from "@/utils/storage";
import { getThemeSetting } from "@/api/common.js";
import {
applyTabBarStyle,
cacheTheme,
loadCachedTheme,
normalizeTheme,
} from "@/utils/theme";
import {
mapMutations
} from "vuex";
applyTabBarStyle,
cacheTheme,
loadCachedTheme,
normalizeTheme,
} from '@/utils/theme'
import { onLaunch, onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
/**
* 路由监听并删除路由
* https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateTo.html
* */
// #ifdef MP-WEIXIN
wx.onAppRoute((res) => {
})
// #endif
const store = useStore()
export default {
data() {
return {
config,
};
},
// #ifdef MP-WEIXIN
wx.onAppRoute(() => {})
// #endif
onLaunch((val) => {
initTheme()
if (val?.query?.inviter) {
storage.setInviter(val.query.inviter)
}
/**
* 监听返回(页面级 onBackPress 在 App.vue 不生效,保留空实现避免误导)
*/
onLaunch: function(val) {
this.initTheme();
if(val.query.inviter){
storage.setInviter(val.query.inviter)
}
// #ifdef APP-PLUS
plus.globalEvent.addEventListener('newintent', () => {
checkArguments()
})
// #endif
// #ifdef APP-PLUS
// 重点是以下: 一定要监听后台恢复 !一定要
plus.globalEvent.addEventListener("newintent", (e) => {
this.checkArguments(); // 检测启动参数
});
// #endif
// #ifdef MP-WEIXIN
applyUpdateWeChat()
// #endif
})
// #ifdef MP-WEIXIN
this.applyUpdateWeChat();
// #endif
},
onShow(() => {
// #ifndef H5
if (config.enableGetClipboard) {
getClipboard()
}
// #endif
onShow() {
// #ifndef H5
if(this.config.enableGetClipboard){
this.getClipboard();
}
// #endif
// #ifdef APP-PLUS
// #ifdef APP-PLUS
if (storage.getShow()) {
if (uni.getSystemInfoSync().platform == 'ios') {
uni.navigateTo({ url: '/pages/tabbar/screen/fullScreen' })
}
}
// #endif
})
if (storage.getShow()) {
if (uni.getSystemInfoSync().platform == 'ios') {
this.$u.route("/pages/tabbar/screen/fullScreen");
function initTheme() {
const cached = loadCachedTheme()
if (cached) {
store.commit('SET_THEME', cached)
applyTabBarStyle(cached.mainColor)
}
getThemeSetting()
.then((res) => {
if (res.data?.success && res.data.result?.settingValue) {
try {
const raw = JSON.parse(res.data.result.settingValue)
const theme = normalizeTheme(raw)
store.commit('SET_THEME', theme)
cacheTheme(theme)
applyTabBarStyle(theme.mainColor)
} catch (e) {
// 主题配置解析失败时使用缓存
}
}
})
.catch(() => {})
}
}
}
// #endif
},
methods: {
...mapMutations(["login", "SET_THEME"]),
initTheme() {
const cached = loadCachedTheme();
if (cached) {
this.SET_THEME(cached);
applyTabBarStyle(cached.mainColor);
}
getThemeSetting()
.then((res) => {
if (res.data && res.data.success && res.data.result && res.data.result.settingValue) {
try {
const raw = JSON.parse(res.data.result.settingValue);
const theme = normalizeTheme(raw);
this.SET_THEME(theme);
cacheTheme(theme);
applyTabBarStyle(theme.mainColor);
} catch (e) {
// 主题配置解析失败时使用缓存
}
}
})
.catch(() => {});
},
/**
* 微信小程序版本提交更新版本 解决缓存问题
*/
applyUpdateWeChat() {
const updateManager = uni.getUpdateManager();
function applyUpdateWeChat() {
const updateManager = uni.getUpdateManager()
updateManager.onCheckForUpdate(function(res) {
// 请求完新版本信息的回调
});
updateManager.onCheckForUpdate(() => {})
updateManager.onUpdateReady(function(res) {
uni.showModal({
title: "更新提示",
content: "发现新版本,是否重启应用?",
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
}
},
});
});
updateManager.onUpdateFailed(function(res) {
// 新的版本下载失败
});
},
updateManager.onUpdateReady(() => {
uni.showModal({
title: '更新提示',
content: '发现新版本,是否重启应用?',
success(res) {
if (res.confirm) {
updateManager.applyUpdate()
}
},
})
})
// TODO 开屏广告 后续优化添加
launch() {
try {
// 获取本地存储中launchFlag标识 开屏广告
const value = uni.getStorageSync("launchFlag");
if (!value) {
// this.$u.route("/pages/index/agreement");
} else {
//app启动时打开启动广告页
var w = plus.webview.open(
"/hybrid/html/advertise/advertise.html",
"本地地址", {
top: 0,
bottom: 0,
zindex: 999,
},
"fade-in",
500
);
//设置定时器4s后关闭启动广告页
setTimeout(function() {
plus.webview.close(w);
APPUpdate();
}, 3000);
}
} catch (e) {
// error
uni.setStorage({
key: "launchFlag",
data: true,
success: function() {
console.log("error时存储launchFlag");
},
});
}
},
updateManager.onUpdateFailed(() => {})
}
/**
* 获取粘贴板数据
*/
async getClipboard() {
let res = await getClipboardData();
function launch() {
try {
const value = uni.getStorageSync('launchFlag')
if (!value) {
// uni.navigateTo({ url: '/pages/index/agreement' })
} else {
const w = plus.webview.open(
'/hybrid/html/advertise/advertise.html',
'本地地址',
{
top: 0,
bottom: 0,
zindex: 999,
},
'fade-in',
500,
)
setTimeout(() => {
plus.webview.close(w)
APPUpdate()
}, 3000)
}
} catch (e) {
uni.setStorage({
key: 'launchFlag',
data: true,
success() {
console.log('error时存储launchFlag')
},
})
}
}
/**
* 解析粘贴板数据
*/
async function getClipboard() {
const res = await getClipboardData()
if (res.indexOf(config.shareLink) != -1 && res != store.state.shareLink) {
store.state.shareLink = res
uni.showModal({
title: '提示',
content: '检测到一个分享链接是否跳转?',
confirmText: '跳转',
success(callback) {
if (callback.confirm) {
const path = res.split(config.shareLink)[1]
if (path.indexOf('tabbar') != -1) {
uni.switchTab({ url: path })
} else {
uni.navigateTo({ url: path })
}
}
},
})
}
}
if (res.indexOf(config.shareLink) != -1 && (res != this.$store.state.shareLink)) {
this.$store.state.shareLink = res
uni.showModal({
title: "提示",
content: "检测到一个分享链接是否跳转?",
confirmText: "跳转",
success: function(callback) {
if (callback.confirm) {
const path = res.split(config.shareLink)[1];
if (path.indexOf("tabbar") != -1) {
uni.switchTab({
url: path,
});
} else {
uni.navigateTo({
url: path,
});
}
}
},
});
}
},
/**
* h5中打开app获取跳转app的链接并跳转
*/
checkArguments() {
// #ifdef APP-PLUS
setTimeout(() => {
const args = plus.runtime.arguments;
if (args) {
const argsStr = decodeURIComponent(args);
const path = argsStr.split("//")[1];
if (path.indexOf("tabbar") != -1) {
uni.switchTab({
url: `/${path}`,
});
} else {
uni.navigateTo({
url: `/${path}`,
});
}
}
});
// #endif
},
},
};
function checkArguments() {
// #ifdef APP-PLUS
setTimeout(() => {
const args = plus.runtime.arguments
if (args) {
const argsStr = decodeURIComponent(args)
const path = argsStr.split('//')[1]
if (path.indexOf('tabbar') != -1) {
uni.switchTab({ url: `/${path}` })
} else {
uni.navigateTo({ url: `/${path}` })
}
}
})
// #endif
}
</script>
<style lang="scss">
@import "uview-plus/index.scss";
@import "uview-plus/index.scss";
@import "@/styles/uview-form-fix.scss";
// -------适配底部安全区 苹果x系列刘海屏
// -------适配底部安全区 苹果x系列刘海屏
// #ifdef MP-WEIXIN
.mp-iphonex-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
box-sizing: content-box;
height: auto !important;
padding-top: 10rpx;
}
// #ifdef MP-WEIXIN
.mp-iphonex-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
box-sizing: content-box;
height: auto !important;
padding-top: 10rpx;
}
// #endif
// #endif
body {
background-color: $bg-color;
}
body {
background-color: $bg-color;
}
/************************ */
.w200 {
width: 200rpx !important;
}
/************************ */
.w200 {
width: 200rpx !important;
}
.flex1 {
flex: 1; //必须父级设置flex
}
.flex1 {
flex: 1; //必须父级设置flex
}
</style>

View File

@@ -10,40 +10,30 @@
</view>
</template>
<script>
export default {
props: {
// 购物车 cart 消息 msg 订单 order 查询 search
type: {
type: String,
default: 'search'
},
isBtn:{
type: Boolean,
default: false
},
title:{
type: String,
default: '没有相关内容'
}
},
data() {
return {
src:''
};
},
mounted() {
this.src ='/static/default/default_'+ this.type + '.png';
},
methods: {
toHome() {
uni.switchTab({
url: '/pages/home/home'
});
}
}
};
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const props = withDefaults(defineProps<{
type?: string
isBtn?: boolean
title?: string
}>(), {
type: 'search',
isBtn: false,
title: '没有相关内容'
})
const src = ref('')
onMounted(() => {
src.value = `/static/default/default_${props.type}.png`
})
const toHome = () => {
uni.switchTab({
url: '/pages/home/home'
})
}
</script>
<style lang="scss" scoped>

View File

@@ -13,72 +13,62 @@
</view>
</template>
<script>
import config from "@/config/config";
import logoImg from "@/icon.png";
export default {
data() {
return {
config, // 设置工具类
weChat: false, // 是否微信浏览器该项为true时不显示 当前整个页面
logo: logoImg, //显示的圆形logo
};
},
mounted() {
// #ifdef H5
// 判断是否是微信浏览器
var ua = navigator.userAgent.toLowerCase();
var isWeixin = ua.indexOf("micromessenger") != -1;
if (isWeixin) {
this.weChat = true;
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import config from "@/config/config"
import logoImg from "@/icon.png"
const weChat = ref(false)
const logo = logoImg
onMounted(() => {
// #ifdef H5
// 判断是否是微信浏览器
var ua = navigator.userAgent.toLowerCase();
var isWeixin = ua.indexOf("micromessenger") != -1;
weChat.value = isWeixin ? true : false
// #endif
})
/**
* 跳转到下载app页面
*/
const downloadApp = () => {
setTimeout(function () {
window.location.href = config.downloadLink;
}, 2000);
}
/**
* 打开app 仅在h5生效 使用ifream唤醒app
*/
const openApp = () => {
let src: string = ''
if (location.href) {
src = location.href.split("/pages")[1];
}
let t = `${config.schemeLink}pages${src}`
try {
var e = navigator.userAgent.toLowerCase(),
n = e.match(/cpu iphone os (.*?) like mac os/);
if (
((n = null !== n ? n[1].replace(/_/g, ".") : 0), parseInt(n) >= 9)
) {
window.location.href = t
downloadApp()
} else {
this.weChat = false;
var r = document.createElement("iframe");
(r.src = t), (r.style.display = "none"), document.body.appendChild(r)
downloadApp()
}
// #endif
},
methods: {
/**
* 跳转到下载app页面
*/
downloadApp() {
setTimeout(function () {
window.location.href = config.downloadLink;
}, 2000);
},
/**
* 打开app 仅在h5生效 使用ifream唤醒app
*/
openApp() {
let src;
if (location.href) {
src = location.href.split("/pages")[1];
}
let t = `${config.schemeLink}pages${src}`;
try {
var e = navigator.userAgent.toLowerCase(),
n = e.match(/cpu iphone os (.*?) like mac os/);
if (
((n = null !== n ? n[1].replace(/_/g, ".") : 0), parseInt(n) >= 9)
) {
window.location.href = t;
this.downloadApp();
} else {
var r = document.createElement("iframe");
(r.src = t), (r.style.display = "none"), document.body.appendChild(r);
this.downloadApp();
}
} catch (e) {
window.location.href = t;
this.downloadApp();
}
},
},
};
} catch (e) {
window.location.href = t
downloadApp()
}
}
</script>
<style scoped lang="scss">

View File

@@ -82,19 +82,19 @@
<!-- 拼团购买仅筛选出当前拼团类型商品 -->
<template v-for="(spec_val, spec_index) in spec.values" :key="spec_index">
<view
v-if="parentOrder && spec_val.skuId == goodsDetail.id"
:class="{ active: spec_val.value == currentSelected[specIndex] }"
class="skus-view-item"
@click="handleClickSpec(spec, specIndex, spec_val)"
>
{{ spec_val.value }}
</view>
<view
v-if="parentOrder && spec_val.skuId == goodsDetail.id"
:class="{ active: spec_val.value == currentSelected[specIndex] }"
class="skus-view-item"
@click="handleClickSpec(spec, specIndex, spec_val)"
>
{{ spec_val.value }}
</view>
</template>
</view>
</view>
<div class="soldout" v-if="goodsDetail.quantity === 0">
<u-alert-tips type="warning" title="商品已售罄" description="当前商品库存为0"></u-alert-tips>
<u-alert type="warning" title="商品已售罄" :description="'当前商品库存为0'"></u-alert>
</div>
<!-- 数量 -->
<view v-if="goodsDetail.quantity !== 0" class="goods-skus-number flex flex-a-c flex-j-sb">
@@ -111,313 +111,269 @@
</u-popup>
</div>
</template>
<script>
import * as API_trade from '@/api/trade.js';
import setup from './popup';
import uniNumberBox from '@/components/uni-number-box'
export default {
components: {
uniNumberBox
},
data() {
return {
setup,
num: this.wholesaleList && this.wholesaleList.length > 0 ? this.wholesaleList[0].num : 1,
selectName: '', //选中商品的昵称
selectSkuList: '', //选中商铺sku,
selectedSpecImg: '', //选中的图片路径
buyType: '', //用于存储促销,拼团等活动类型
parentOrder: '', //父级拼团活动的数据 - 如果是团员则有数据
formatList: [],
currentSelected: [],
skuList: '',
isClose: false //是否可以点击遮罩关闭
};
},
props: {
wholesaleList: {
type: null,
default: false
},
buyMask: {
type: Boolean,
default: false
},
isGroup: {
type: Boolean,
default: false
},
goodsDetail: {
default: '',
type: null
},
selectedSku: {
default: '',
type: null
},
goodsSpec: {
default: '',
type: null
},
addr: {
default: '',
type: null
},
pointDetail: {
default: '',
type: null
<script setup lang="ts">
import { ref, reactive, watch, onMounted } from 'vue'
import * as API_trade from '@/api/trade.js'
import setup from './popup.js'
import uniNumberBox from '@/components/uni-number-box.vue'
import { goodsFormatPrice } from '@/utils/filters.js'
const props = withDefaults(defineProps<{
wholesaleList?: any[] | boolean
buyMask?: boolean
isGroup?: boolean
goodsDetail?: any
selectedSku?: any | null
goodsSpec?: any | null
addr?: any | null
pointDetail?: any | null
}>(), {
wholesaleList: false,
buyMask: false,
isGroup: false,
goodsDetail: () => ({}),
selectedSku: '',
goodsSpec: '',
addr: '',
pointDetail: ''
})
const emit = defineEmits(['closeBuy', 'changed', 'handleClickSku', 'queryCart'])
const num = ref(props.wholesaleList && Array.isArray(props.wholesaleList) && props.wholesaleList.length > 0 ? props.wholesaleList[0].num : 1)
const selectName = ref('')
const selectSkuList = ref<any>('')
const selectedSpecImg = ref('')
const buyType = ref('')
const parentOrder = ref<any>('')
const formatList = ref<any[]>([])
const currentSelected = ref<string[]>([])
const skuList = ref('')
watch(num, (val) => {
val == 0 ? num.value = 1 : ''
if (val) {
//超过库存后修改回库存
if (val > props.goodsDetail.quantity) {
num.value = props.goodsDetail.quantity
}
},
computed: {
wholesalePrice(key) {
return this.wholesaleList.length
? this.wholesaleList.map(item => {
return item.price;
})
: [];
},
wholesaleNum(key) {
return this.wholesaleList.length
? this.wholesaleList.map(item => {
return item.num;
})
: [];
}
},
watch: {
num(val) {
val == 0 ? this.num = 1 : ''
if (val) {
//超过库存后修改回库存
if (val > this.goodsDetail.quantity) {
this.$nextTick(function() {
this.num = this.goodsDetail.quantity;
});
}
}
},
buyType: {
handler(val) {
if (val) {
this.buyType = val;
}
},
immediate: true
},
selectSkuList: {
handler(val, oldval) {
this.$emit('changed', val);
},
deep: true
},
'goodsDetail.quantity': {
handler(val) {
if (val == 0) {
uni.showToast({
title: '商品已售罄',
duration: 2000,
icon: 'none'
})
this.num = 1;
}
}
}
},
methods: {
numCheck(val) {
if (this.wholesaleList && this.wholesaleList.length > 0) {
if (this.num <= this.wholesaleList[0].num) {
uni.showToast({
title: '批发商品购买数量不能小于起批数量!',
duration: 2000,
icon: 'none'
});
this.num = this.wholesaleList[0].num;
}
}
},
closeMask() {
this.$emit('closeBuy', false);
},
/**点击规格 */
handleClickSpec(val, index, specValue) {
this.currentSelected[index] = specValue.value;
let selectedSkuId = this.goodsSpec.find(i => {
let matched = true;
let specValues = i.specValues.filter(j => j.specName !== 'images');
for (let n = 0; n < specValues.length; n++) {
if (specValues[n].specValue !== this.currentSelected[n]) {
matched = false;
return;
}
}
if (matched) {
return i;
}
});
if (selectedSkuId?.skuId) {
this.currentSelected[index] = specValue.value;
this.selectSkuList = {
spec: {
specName: val.name,
specValue: specValue.value
},
data: this.goodsDetail
};
this.selectName = specValue.value;
this.$emit('handleClickSku', {
skuId: selectedSkuId.skuId,
goodsId: this.goodsDetail.goodsId
});
} else {
uni.showToast({
title: '暂无该商品!',
duration: 2000,
icon: 'none'
});
}
},
/**
* 直接购买
*/
buy(data) {
API_trade.addToCart(data).then(res => {
if (res.data.success) {
uni.navigateTo({
url: `/pages/order/fillorder?way=${data.cartType}&addr=${''}&parentOrder=${encodeURIComponent(JSON.stringify(this.parentOrder))}`
});
}
});
},
/**
* 添加到购物车或购买
*/
addToCartOrBuy(val) {
if (!this.selectSkuList) {
uni.showToast({
title: '请选择规格商品',
icon: 'none'
});
return;
}
let data = {
skuId: this.goodsDetail.id,
num: this.num
};
if (val == 'cart') {
API_trade.addToCart(data).then(res => {
if (res.data.code == 200) {
uni.showToast({
title: '商品已添加到购物车',
icon: 'none'
});
this.$emit('queryCart');
this.closeMask();
}
});
} else {
// 判断是否拼团商品
if (this.buyType) {
data.cartType = 'PINTUAN';
} else if (this.goodsDetail.goodsType == 'VIRTUAL_GOODS') {
data.cartType = 'VIRTUAL';
} else {
data.cartType = 'BUY_NOW';
}
API_trade.addToCart(data).then(res => {
if (res.data.code == 200) {
uni.navigateTo({
url: `/pages/order/fillorder?way=${data.cartType}&addr=${this.addr.id || ''}&parentOrder=${encodeURIComponent(JSON.stringify(this.parentOrder))}`
});
}
});
}
},
formatSku(list) {
// 格式化数据
let arr = [{}];
if (!Array.isArray(list)) {
return false;
}
list.forEach((item, index) => {
item.specValues.forEach((spec, specIndex) => {
let name = spec.specName;
let values = {
value: spec.specValue,
quantity: item.quantity,
skuId: item.skuId
};
if (name === 'images') {
return;
}
arr.forEach((arrItem, arrIndex) => {
if (
arrItem.name == name &&
arrItem.values &&
!arrItem.values.find(i => {
return i.value === values.value;
})
) {
arrItem.values.push(values);
}
let keys = arr.map(key => {
return key.name;
});
if (!keys.includes(name)) {
arr.push({
name: name,
values: [values]
});
}
});
});
});
arr.shift();
this.formatList = arr;
list.forEach(item => {
// 默认选中
if (item.skuId === this.goodsDetail.id) {
item.specValues
.filter(i => i.specName !== 'images')
.forEach((value, _index) => {
this.currentSelected[_index] = value.specValue;
this.selectName = value.specValue;
this.selectSkuList = {
spec: value,
data: this.goodsDetail
};
});
}
});
this.skuList = list;
// console.log(" this.skuList", this.skuList)
}
},
mounted() {
this.formatSku(this.goodsSpec);
}
};
})
watch(() => props.buyType, (val) => {
if (val) {
buyType.value = val as string
}
}, { immediate: true })
watch(selectSkuList, (_val) => {
emit('changed', selectSkuList.value)
}, { deep: true })
watch(() => props.goodsDetail?.quantity, (val) => {
if (val == 0) {
uni.showToast({
title: '商品已售罄',
duration: 2000,
icon: 'none'
})
num.value = 1
}
})
const numCheck = (val: number) => {
if (Array.isArray(props.wholesaleList) && props.wholesaleList.length > 0) {
if (num.value <= props.wholesaleList[0].num) {
uni.showToast({
title: '批发商品购买数量不能小于起批数量!',
duration: 2000,
icon: 'none'
})
num.value = props.wholesaleList[0].num
}
}
}
const closeMask = () => {
emit('closeBuy', false)
}
/**点击规格 */
const handleClickSpec = (val: any, index: number, specValue: any) => {
currentSelected.value[index] = specValue.value
let selectedSkuId = props.goodsSpec.find((i: any) => {
let matched = true
let specValues = i.specValues.filter((j: any) => j.specName !== 'images')
for (let n = 0; n < specValues.length; n++) {
if (specValues[n].specValue !== currentSelected.value[n]) {
matched = false
return
}
}
if (matched) {
return i
}
})
if (selectedSkuId?.skuId) {
currentSelected.value[index] = specValue.value
selectSkuList.value = {
spec: {
specName: val.name,
specValue: specValue.value
},
data: props.goodsDetail
}
selectName.value = specValue.value
emit('handleClickSku', {
skuId: selectedSkuId.skuId,
goodsId: props.goodsDetail.goodsId
})
} else {
uni.showToast({
title: '暂无该商品!',
duration: 2000,
icon: 'none'
})
}
}
/**
* 直接购买
*/
const buy = (data: any) => {
API_trade.addToCart(data).then(res => {
if (res.data.success) {
uni.navigateTo({
url: `/pages/order/fillorder?way=${data.cartType}&addr=${''}&parentOrder=${encodeURIComponent(JSON.stringify(parentOrder.value))}`
})
}
})
}
/**
* 添加到购物车或购买
*/
const addToCartOrBuy = (val: string) => {
if (!selectSkuList.value) {
uni.showToast({
title: '请选择规格商品',
icon: 'none'
})
return
}
let data = {
skuId: props.goodsDetail.id,
num: num.value
}
if (val == 'cart') {
API_trade.addToCart(data).then(res => {
if (res.data.code == 200) {
uni.showToast({
title: '商品已添加到购物车',
icon: 'none'
})
emit('queryCart')
closeMask()
}
})
} else {
// 判断是否拼团商品
if (buyType.value) {
data.cartType = 'PINTUAN'
} else if (props.goodsDetail.goodsType == 'VIRTUAL_GOODS') {
data.cartType = 'VIRTUAL'
} else {
data.cartType = 'BUY_NOW'
}
API_trade.addToCart(data).then(res => {
if (res.data.code == 200) {
uni.navigateTo({
url: `/pages/order/fillorder?way=${data.cartType}&addr=${props.addr?.id || ''}&parentOrder=${encodeURIComponent(JSON.stringify(parentOrder.value))}`
})
}
})
}
}
const formatSku = (list: any[]) => {
// 格式化数据
let arr: any[] = [{}]
if (!Array.isArray(list)) {
return false
}
list.forEach((_item, _index) => {
let item = list[_index]
item.specValues.forEach((spec: any, specIndex: number) => {
let name = spec.specName
let values = {
value: spec.specValue,
quantity: item.quantity,
skuId: item.skuId
}
if (name === 'images') {
return
}
arr.forEach((arrItem, arrIndex) => {
if (
arrItem.name == name &&
arrItem.values &&
!arrItem.values.find((i: any) => {
return i.value === values.value
})
) {
arrItem.values.push(values)
}
let keys = arr.map(key => {
return key.name
})
if (!keys.includes(name)) {
arr.push({
name: name,
values: [values]
})
}
})
})
})
arr.shift()
formatList.value = arr
list.forEach(item => {
// 默认选中
if (item.skuId === props.goodsDetail.id) {
item.specValues
.filter(i => i.specName !== 'images')
.forEach((value, _index) => {
currentSelected.value[_index] = value.specValue
selectName.value = value.specValue
selectSkuList.value = {
spec: value,
data: props.goodsDetail
}
})
}
})
skuList.value = list
}
onMounted(() => {
formatSku(props.goodsSpec)
})
</script>
<style lang="scss" scoped>
@import './popup.scss';

View File

@@ -17,168 +17,156 @@
</div>
</template>
<script>
<script setup lang="ts">
import { ref, onMounted, getCurrentInstance } from 'vue'
// 引入绘制插件
import DrawPoster from "@/js_sdk/u-draw-poster";
import logoImg from "@/pages/passport/static/logo-title.png";
import DrawPoster from "@/js_sdk/u-draw-poster"
import logoImg from "@/pages/passport/static/logo-title.png"
export default {
data: () => ({
imgUrl: "", //绘制出来的图片路径
show: false, //是否展示模态框
dp: {}, //绘制的dp对象用于存储绘制等一些方法。
logo: logoImg, //本地logo地址
}),
const instance = getCurrentInstance()
props: {
/**
* 父级传参的数据
*/
res: {
type: null,
default: "",
const props = defineProps<{
res?: any
}>()
const imgUrl = ref("")
const show = ref(false)
const dp = ref<any>({})
const logo = logoImg
/**
* 解决微信小程序中图片模糊问题
*/
// #ifdef MP-WEIXIN
const st2 = (size: number) => size * 2
// #endif
// #ifndef MP-WEIXIN
const st2 = (size: number) => size
// #endif
/**
* 保存图片
*/
const downLoad = () => {
uni.saveImageToPhotosAlbum({
filePath: imgUrl.value,
success: function () {
uni.showToast({
title: "保存成功!",
icon: "none",
})
},
},
onUnload() {},
methods: {
/**
* 解决微信小程序中图片模糊问题
*/
// #ifdef MP-WEIXIN
st2: (size) => size * 2,
// #endif
// #ifndef MP-WEIXIN
st2: (size) => size,
// #endif
/**
* 保存图片
*/
downLoad() {
uni.saveImageToPhotosAlbum({
filePath: this.imgUrl,
success: function () {
uni.showToast({
title: "保存成功!",
icon: "none",
});
},
fail: function () {
uni.showToast({
title: "保存失败,请稍后重试!",
icon: "none",
});
},
});
fail: function () {
uni.showToast({
title: "保存失败,请稍后重试!",
icon: "none",
})
},
})
}
/**
* 创建canvas
*/
async init() {
this.show = true;
this.dp = await DrawPoster.build({
selector: "canvas",
componentThis: this,
loading: true,
debugging: true,
});
let dp = this.dp;
// #ifdef MP-WEIXIN
// 用于微信小程序中画布错乱问题
dp.canvas.width = this.st2(600);
dp.canvas.height = this.st2(960);
// #endif
this.draw(dp);
},
/**
* 创建canvas
*/
const init = async () => {
show.value = true
dp.value = await DrawPoster.build({
selector: "canvas",
componentThis: instance?.proxy,
loading: true,
debugging: true,
})
let dpVal = dp.value
// #ifdef MP-WEIXIN
// 用于微信小程序中画布错乱问题
dpVal.canvas.width = st2(600)
dpVal.canvas.height = st2(960)
// #endif
await draw(dpVal)
}
async draw(dp) {
const { width, height, background, title } = this.res.container;
const { code, img, price } = this.res.bottom;
const draw = async (dp: any) => {
const { width, height, background, title } = props.res.container
const { code, img, price } = props.res.bottom
// /** 绘制背景 */
await dp.draw((ctx) => {
ctx.fillStyle = background;
ctx.fillRoundRect(
this.st2(0),
this.st2(0),
this.st2(width),
this.st2(height),
this.st2(12)
);
ctx.clip();
});
/** 绘制图片 */
dp.draw(async (ctx) => {
await Promise.all([
// 绘制Logo
ctx.drawImage(
this.logo,
this.st2(175),
this.st2(0),
this.st2(256),
this.st2(144)
),
// 中间图片
ctx.drawImage(
img,
this.st2(100),
this.st2(150),
this.st2(400),
this.st2(400)
),
// 二维码
ctx.drawImage(
code,
this.st2(39),
this.st2(750),
this.st2(150),
this.st2(150)
),
]);
});
// /** 绘制背景 */
await dp.draw((ctx: any) => {
ctx.fillStyle = background
ctx.fillRoundRect(
st2(0),
st2(0),
st2(width),
st2(height),
st2(12)
)
ctx.clip()
})
/** 绘制图片 */
dp.draw(async (ctx: any) => {
await Promise.all([
// 绘制Logo
ctx.drawImage(
logo,
st2(175),
st2(0),
st2(256),
st2(144)
),
// 中间图片
ctx.drawImage(
img,
st2(100),
st2(150),
st2(400),
st2(400)
),
// 二维码
ctx.drawImage(
code,
st2(39),
st2(750),
st2(150),
st2(150)
),
])
})
/** 绘制中间文字*/
await dp.draw((ctx) => {
ctx.fillStyle = "#333";
ctx.font = `bold ${this.st2(24)}px PingFang SC`;
ctx.textAlign = "center";
ctx.fillWarpText({
text: title,
maxWidth: this.st2(500),
x: this.st2(300),
y: this.st2(600),
layer: 1,
});
/** 绘制中间文字*/
await dp.draw((ctx: any) => {
ctx.fillStyle = "#333"
ctx.font = `bold ${st2(24)}px PingFang SC`
ctx.textAlign = "center"
ctx.fillWarpText({
text: title,
maxWidth: st2(500),
x: st2(300),
y: st2(600),
layer: 1,
})
ctx.fillStyle = "#ff3c2a";
ctx.font = `${this.st2(38)}px PingFang SC`;
ctx.textAlign = "center";
ctx.fillText(price, this.st2(300), this.st2(680));
});
ctx.fillStyle = "#ff3c2a"
ctx.font = `${st2(38)}px PingFang SC`
ctx.textAlign = "center"
ctx.fillText(price, st2(300), st2(680))
})
// /** 绘制底部文字 */
await dp.draw((ctx) => {
ctx.fillStyle = "#666";
ctx.font = `${this.st2(24)}px PingFang SC`;
ctx.fillText("长按图片,识别二维码", this.st2(200), this.st2(866));
ctx.fillStyle = "#666";
ctx.font = `${this.st2(24)}px PingFang SC`;
ctx.fillText("查看商品详情", this.st2(200), this.st2(900));
});
// /** 绘制底部文字 */
await dp.draw((ctx: any) => {
ctx.fillStyle = "#666"
ctx.font = `${st2(24)}px PingFang SC`
ctx.fillText("长按图片,识别二维码", st2(200), st2(866))
ctx.fillStyle = "#666"
ctx.font = `${st2(24)}px PingFang SC`
ctx.fillText("查看商品详情", st2(200), st2(900))
})
this.imgUrl = await dp.createImagePath();
imgUrl.value = await dp.createImagePath()
}
// console.log(posterImgUrl)
},
},
async mounted() {
this.init();
},
};
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>

View File

@@ -79,192 +79,182 @@
</view>
</template>
<script>
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
import { getRegionsById } from "@/api/common.js"
const instance = getCurrentInstance()
let windowWidth = 0
import { getRegionsById } from "@/api/common.js";
export default {
name: "UniCityNvue",
props: {
headTitle: {
//标题
type: String,
default: "区域选择",
},
pickerSize: {
// 使用多少个tab
type: [String, String],
default: "1",
},
provinceData: {
// 默认的省市区id如果不使用id的情况下则为[]
type: Array,
default: function () {
return [];
const props = withDefaults(defineProps<{
headTitle?: string
pickerSize?: string | string[]
provinceData?: any[]
}>(), {
headTitle: "区域选择",
pickerSize: "1",
provinceData: () => [],
})
const emit = defineEmits(['funcValue'])
const clearRightIcon = ref(true)
const scrollLeft = ref(500)
const scrollTop = ref(0)
const enableScroll = ref(true)
const tabCurrentIndex = ref(0)
const tabbars = ref<any[]>(props.provinceData)
const pickersize = ref(props.pickerSize)
const showPicker = ref(false)
/**
* 显示选择器
*/
const show = () => {
showPicker.value = true
if (tabbars.value[0].children.length == 0) {
getRegionsById(0).then((res: any) => {
tabbars.value[0].children = res.data.result
})
}
windowWidth = uni.getSystemInfoSync().windowWidth
}
/**
* 关闭选择器
*/
const hide = () => {
showPicker.value = false
}
/**
* tab切换
*/
const changeTab = (e: number) => {
let index = e
setScroll(index)
//延迟300ms,等待swiper动画结束再修改tabbar
tabCurrentIndex.value = index
setTimeout(() => {
getScroll("show" + index)
}, 10)
}
/**
* 获得元素的大小
*/
const getElSize = (id: string): Promise<any> => {
return new Promise((res) => {
let el = uni
.createSelectorQuery()
.in(instance!.proxy)
.select("#" + id)
el.fields(
{
size: true,
scrollOffset: true,
rect: true,
},
},
},
data() {
return {
clearRightIcon: true, //是否显示右侧关闭icon
scrollLeft: 500, //顶部选项卡左滑距离
scrollTop: 0, //默认滚动顶部为0
enableScroll: true, //是否启用滚动
tabCurrentIndex: 0, //当前选项卡索引
tabbars: this.provinceData, //默认的省市区id
pickersize: this.pickerSize, //多少个tab 推荐为4级
showPicker: false, //显示选取器
};
},
methods: {
/**
* 显示选择器
*/
show() {
this.showPicker = true;
if (this.tabbars[0].children.length == 0) {
getRegionsById(0).then((res) => {
this.tabbars[0].children = res.data.result;
});
(data: any) => {
res(data)
}
).exec()
})
}
windowWidth = uni.getSystemInfoSync().windowWidth;
},
/**
* 关闭选择器
*/
hide() {
this.showPicker = false;
},
/**
* tab切换
*/
changeTab(e) {
let index = e;
this.setScroll(index);
//延迟300ms,等待swiper动画结束再修改tabbar
this.tabCurrentIndex = index;
setTimeout(() => {
this.getScroll("show" + index);
}, 10);
},
/**
* 获得元素的大小
*/
getElSize(id) {
return new Promise((res, rej) => {
let el = uni
.createSelectorQuery()
.in(this)
.select("#" + id);
el.fields(
{
size: true,
scrollOffset: true,
rect: true,
},
(data) => {
res(data);
}
).exec();
});
},
/**
* 点击城市后回调
*/
async changCity(index, item) {
if (this.tabbars[index].id != item.id) {
this.tabbars[index].localName = item.name;
this.tabbars[index].id = item.id;
this.tabbars[index].center = item.center
if (index < this.tabbars.length - 1) {
this.tabbars.splice(index + 1, this.tabbars.length - index - 1);
}
if (this.tabbars.length < this.pickersize) {
uni.showLoading({
title: "加载中",
mask:true
});
try {
let data = await getRegionsById(item.id);
uni.hideLoading();
// 当前选项级为最后一级时回调,将选中的数据返回
if (data.data.result.length == 0) {
this.$emit("funcValue", this.tabbars);
this.hide();
} else {
// 将新的数据填充进下一级
var current = {
localName: "请选择",
id: "",
children: data.data.result,
};
this.tabbars.push(current);
this.tabCurrentIndex++;
// 当前距离重新为最上面
this['scrollTop'] = 0
}
} catch (error) {
uni.hideLoading();
}
/**
* 点击城市后回调
*/
const changCity = async (index: number, item: any) => {
if (tabbars.value[index].id != item.id) {
tabbars.value[index].localName = item.name
tabbars.value[index].id = item.id
tabbars.value[index].center = item.center
if (index < tabbars.value.length - 1) {
tabbars.value.splice(index + 1, tabbars.value.length - index - 1)
}
if (tabbars.value.length < pickersize.value as number) {
uni.showLoading({
title: "加载中",
mask: true
})
try {
let data = await getRegionsById(item.id)
uni.hideLoading()
// 当前选项级为最后一级时回调,将选中的数据返回
if (data.data.result.length == 0) {
emit("funcValue", tabbars.value)
hide()
} else {
this.$emit("funcValue", this.tabbars);
this.hide();
}
}
},
// 将新的数据填充进下一级
var current = {
localName: "请选择",
id: "",
children: data.data.result,
}
tabbars.value.push(current)
tabCurrentIndex.value++
/**
* 获取当前tab中滚动的距离
*/
async setScroll(index) {
let width = 0;
let nowWidth = 0;
for (let i = 0; i <= index; i++) {
let result = await this.getElSize("tab" + i);
width += result.width;
if (i === index) {
nowWidth = result.width;
// 当前距离重新为最上面
scrollTop.value = 0
}
} catch (error) {
uni.hideLoading()
}
if (width + nowWidth > windowWidth) {
this.scrollLeft = width + nowWidth;
} else {
this.scrollLeft = 0;
}
},
} else {
emit("funcValue", tabbars.value)
hide()
}
}
}
/**
* 计算当前的滚动距离
*/
getScroll(id) {
/**
* 获取当前tab中滚动距离
*/
const setScroll = async (index: number) => {
let width = 0
let nowWidth = 0
for (let i = 0; i <= index; i++) {
let result = await getElSize("tab" + i)
width += result.width
if (i === index) {
nowWidth = result.width
}
}
if (width + nowWidth > windowWidth) {
scrollLeft.value = width + nowWidth
} else {
scrollLeft.value = 0
}
}
/**
* 计算当前的滚动距离
*/
const getScroll = (id: string) => {
uni
.createSelectorQuery()
.in(instance!.proxy)
.select(".panel-scroll-box")
.boundingClientRect((data: any) => {
uni
.createSelectorQuery()
.in(this)
.select(".panel-scroll-box")
.boundingClientRect((data) => {
uni
.createSelectorQuery()
.in(this)
.select("#" + id)
.boundingClientRect((res) => {
if (res != undefined && res != null && res != "") {
this.scrollTop = res.top - data.top;
}
})
.exec();
.in(instance!.proxy)
.select("#" + id)
.boundingClientRect((res: any) => {
if (res != undefined && res != null && res != "") {
scrollTop.value = res.top - data.top
}
})
.exec();
},
},
};
.exec()
})
.exec()
}
// 暴露方法给父组件调用
defineExpose({ show, hide })
</script>
<style lang="scss" scoped>

View File

@@ -1,5 +1,5 @@
<template>
<div>
<view>
<!-- 一行两列商品展示 -->
<view class="goods-list" v-if="type == 'twoColumns'">
<view v-for="(item, index) in res" :key="index" class="goods-item">
@@ -10,7 +10,7 @@
height="330rpx"
mode="aspectFit"
>
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</view>
<view class="goods-detail">
@@ -57,7 +57,7 @@
</view>
<!-- 一行一列商品展示 -->
<div v-if="type == 'oneColumns'">
<div v-for="(item, index) in res" :key="index" class="goods-row">
div v-for="(item, index) in res" :key="index" class="goods-row">
<div class="flex goods-col">
<div class="goods-img" @click="navigateToDetailPage(item)">
<u-image
@@ -67,7 +67,7 @@
mode="aspectFit"
:src="item.goodsImage || item.thumbnail"
>
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</div>
<div class="goods-detail">
@@ -112,41 +112,26 @@
</div>
</div>
</div>
</div>
</view>
</template>
<script>
import commonTpl from "@/components/m-goods-list/common";
export default {
data() {
return {
lightColor: this.$mainColor,
};
},
mixins: [commonTpl],
props: {
// 展示的类型
type:{
type:String,
default:"oneColumns"
},
// 遍历的数据
res: {
type: Array,
default: () => {
return [];
},
},
},
methods: {
// 跳转到商品详情
navigateToDetailPage(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
},
};
<script setup lang="ts">
import { useGoodsListCommon } from './common'
import { goodsFormatPrice } from '@/utils/filters.js'
const { lightSearchStr, getPromotion, navigateToDetailPage, navigateToStoreDetailPage } = useGoodsListCommon()
const props = withDefaults(defineProps<{
type?: string
res?: any[]
storeName?: boolean
keyword?: string | null
}>(), {
type: 'oneColumns',
res: () => [],
storeName: true,
keyword: ''
})
</script>
<style lang="scss" scoped>

View File

@@ -0,0 +1,90 @@
import { useStore } from '@/store'
/**
* 商品列表公共逻辑(原 common.vue mixin
* 提供高亮搜索、unicode 转换、促销标签、导航跳转等通用方法
*/
export function useGoodsListCommon() {
const store = useStore()
const lightColor = store.getters.lightColor
/**
* 高亮显示搜索内容
*/
const lightSearchStr = (keyword: string, str: string) => {
if (!keyword) {
return str
} else {
let unicodes = ''
for (let i of Array.from(keyword)) {
unicodes += unicode(i) + "|"
}
const rule = '(' + unicodes + ')'
const reg = new RegExp(rule, 'gi')
return str ? str.replace(reg, (matchValue) =>
`<span style="color:${lightColor}">${matchValue}</span>`
) : ''
}
}
/**
* 转换为 unicode
*/
const unicode = (str: string) => {
var value = ''
for (var i = 0; i < str.length; i++) {
value += '\\u' + leftZero4(parseInt(str.charCodeAt(i).toString(16)))
}
return value
}
const leftZero4 = (str: string) => {
if (str != null && str != '' && str != 'undefined') {
if (str.length == 2) {
return '00' + str
}
}
return str
}
/**
* 数据去重 只显示一次 减免 劵等
*/
const getPromotion = (item: any) => {
if (item.promotionMap) {
let array: string[] = []
Object.keys(item.promotionMap).forEach((child) => {
if (!array.includes(child.split("-")[0])) {
array.push(child.split("-")[0])
}
})
return array
}
}
/**
* 跳转到商品详情
*/
const navigateToDetailPage = (item: any) => {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
/**
* 跳转到店铺详情
*/
const navigateToStoreDetailPage = (item: any) => {
uni.navigateTo({
url: `/pages/product/shopPage?id=${item.storeId}`,
})
}
return {
lightSearchStr,
getPromotion,
navigateToDetailPage,
navigateToStoreDetailPage,
}
}

View File

@@ -1,69 +0,0 @@
<template>
</template>
<script>
export default {
methods: {
// 高亮显示搜索内容
lightSearchStr(keyword, str) {
if (!keyword) {
return str
} else {
let unicodes = '';
for (let i of Array.from(keyword)) {
unicodes += this.unicode(i) + "|"
}
const rule = '(' + unicodes + ')'
const reg = new RegExp(rule, 'gi');
return str ? str.replace(reg, matchValue =>
`<span style="color:${this.lightColor}">${matchValue}</span>`
) : ''
}
},
// 转换为unicode
unicode(str) {
var value = '';
for (var i = 0; i < str.length; i++) {
value += '\\u' + this.left_zero_4(parseInt(str.charCodeAt(i)).toString(16));
}
return value;
},
left_zero_4(str) {
if (str != null && str != '' && str != 'undefined') {
if (str.length == 2) {
return '00' + str;
}
}
return str;
},
// 数据去重一下 只显示一次 减免 劵 什么的
getPromotion(item) {
if (item.promotionMap) {
let array = [];
Object.keys(item.promotionMap).forEach((child) => {
if (!array.includes(child.split("-")[0])) {
array.push(child.split("-")[0]);
}
});
return array;
}
},
// 跳转到商品详情
navigateToDetailPage(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
// 跳转地址
navigateToStoreDetailPage(item) {
uni.navigateTo({
url: `/pages/product/shopPage?id=${item.storeId}`,
});
},
},
}
</script>
<style lang='scss' scoped>
</style>

View File

@@ -5,7 +5,7 @@
<view v-for="(item, index) in res" :key="index" class="goods-item">
<view class="image-wrapper" @click="navigateToDetailPage(item)">
<u-image :src="item.thumbnail" width="100%" height='330rpx' mode="aspectFit">
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</view>
<view class="goods-detail">
@@ -51,7 +51,7 @@
<div class="flex goods-col">
<div class="goods-img" @click="navigateToDetailPage(item)">
<u-image width="230rpx" mode="aspectFit" border-radius='16' height="230rpx" :src="item.thumbnail">
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</div>
<div class="goods-detail">
@@ -94,114 +94,32 @@
</view>
</template>
<script>
import commonTpl from '@/components/m-goods-list/common'
export default {
data() {
return {
lightColor: this.$mainColor
}
},
mixins: [commonTpl],
props: {
// 遍历的数据
res: {
type: Array,
default: () => {
return []
}
},
// 一行两列还是一行一列显示
type: {
type: String,
default: 'twoColumns',
validator() {
return ['twoColumns', 'oneColumns']
}
},
storeName: {
type: Boolean,
default: true
},
tabBarGap: {
type: Boolean,
default: true
},
keyword: {
type: null,
default: ''
}
<script setup lang="ts">
import { watch } from 'vue'
import { useGoodsListCommon } from './common'
import { goodsFormatPrice } from '@/utils/filters.js'
},
watch: {
keyword(val) {
if (val) {
this.lightSearchStr(val)
}
}
},
methods: {
const { lightSearchStr, getPromotion, navigateToDetailPage, navigateToStoreDetailPage } = useGoodsListCommon()
// 高亮显示搜索内容
lightSearchStr(keyword, str) {
if (!keyword) {
return str
} else {
let unicodes = '';
for (let i of Array.from(keyword)) {
unicodes += this.unicode(i) + "|"
}
const rule = '(' + unicodes + ')'
const reg = new RegExp(rule, 'gi');
return str ? str.replace(reg, matchValue =>
`<span style="color:${this.lightColor}">${matchValue}</span>`
) : ''
}
},
// 转换为unicode
unicode(str) {
var value = '';
for (var i = 0; i < str.length; i++) {
value += '\\u' + this.left_zero_4(parseInt(str.charCodeAt(i)).toString(16));
}
return value;
},
left_zero_4(str) {
if (str != null && str != '' && str != 'undefined') {
if (str.length == 2) {
return '00' + str;
}
}
return str;
},
// 数据去重一下 只显示一次 减免 劵 什么的
getPromotion(item) {
if (item ? item.promotionMap : item.promotionMap) {
const fieldList = item ? item.promotionMap : item.promotionMap
let array = [];
Object.keys(fieldList).forEach((child) => {
if (!array.includes(child.split("-")[0])) {
array.push(child.split("-")[0]);
}
});
return array;
}
},
// 跳转到商品详情
navigateToDetailPage(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
// 跳转地址
navigateToStoreDetailPage(item) {
uni.navigateTo({
url: `/pages/product/shopPage?id=${item.storeId}`,
});
},
}
const props = withDefaults(defineProps<{
res?: any[]
type?: string
storeName?: boolean
tabBarGap?: boolean
keyword?: string | null
}>(), {
res: () => [],
type: 'twoColumns',
storeName: true,
tabBarGap: true,
keyword: ''
})
watch(() => props.keyword, (val) => {
if (val) {
lightSearchStr(val as string, '')
}
})
</script>
<style lang='scss' scoped>
@@ -212,6 +130,7 @@
.goods-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin: 10rpx 20rpx 284rpx;
width: calc(100% - 40rpx);
box-sizing: border-box;
@@ -219,21 +138,17 @@
&.goods-list--embedded {
margin-bottom: 20rpx;
}
>.goods-item {
background-color: #ffffff;
display: flex;
border-radius: 16rpx;
flex-direction: column;
width: calc(50% - 30rpx);
width: calc(50% - 10rpx);
margin-bottom: 20rpx;
padding-bottom: 20rpx;
&:nth-child(2n + 1) {
margin-right: 20rpx;
}
.image-wrapper {
width: 100%;
height: 330rpx;
@@ -242,12 +157,12 @@
padding: 0;
}
}
.count-config,
.store-seller-name {
font-size: $font-sm;
}
.store-seller-name {
color: #666;
display: flex;
@@ -322,7 +237,7 @@
.goods-detail {
margin: 0 20rpx;
>.title {
font-size: $font-base;
color: $font-color-dark;
@@ -334,7 +249,7 @@
-webkit-line-clamp: 2;
overflow: hidden;
}
.promotion {
margin-top: 4rpx;
display: flex;
@@ -344,10 +259,10 @@
color: $light-color;
margin-right: 10rpx;
padding: 0 4rpx;
border-radius: 2rpx;
border-radius: 2px;
}
}
.store-seller-name {
color: #666;
display: flex;
@@ -409,7 +324,7 @@
padding-right: 10rpx;
font-size: 24rpx;
color: $font-color-light;
>.price {
font-size: 26rpx;
line-height: 1;
@@ -421,6 +336,5 @@
}
}
}
}
</style>

View File

@@ -4,7 +4,7 @@
<div class="flex goods-col">
<div class="goods-img">
<u-image width="230rpx" mode="aspectFit" border-radius='16' height="230rpx" :src="item.goodsImage || item.thumbnail">
<template #loading><u-loading></u-loading></template>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</div>
<div class="goods-detail">
@@ -43,43 +43,33 @@
</div>
</template>
<script>
import commonTpl from '@/components/m-goods-list/common'
export default {
data() {
return {
lightColor: this.$mainColor,
}
},
mixins: [commonTpl],
props: {
// 遍历的数据
res: {
type: Array,
default: () => {
return []
}
},
type:{
type:null,
default:""
}
},
methods: {
// 跳转到商品详情
navigateToDetailPage(item) {
if(this.type == 'kanJia'){
uni.navigateTo({
url: `/pages/promotion/bargain/detail?id=${item.id}`,
});
return
}
uni.navigateTo({
url: `/pages/product/goods?id=${item.skuId}&goodsId=${item.goodsId}`,
});
},
}
<script setup lang="ts">
import { useGoodsListCommon } from './common'
import { goodsFormatPrice } from '@/utils/filters.js'
const { navigateToDetailPage } = useGoodsListCommon()
const props = withDefaults(defineProps<{
res?: any[]
type?: string | null
}>(), {
res: () => [],
type: ''
})
// 跳转到商品详情(覆盖公共方法,增加砍价跳转)
const navigateToDetailPageOverride = (item: any) => {
if(props.type == 'kanJia'){
uni.navigateTo({
url: `/pages/promotion/bargain/detail?id=${item.id}`,
})
return
}
navigateToDetailPage({
id: item.skuId,
goodsId: item.goodsId,
})
}
</script>
<style lang='scss' scoped>
@@ -87,72 +77,72 @@
width: 152rpx;
height: 108rpx;
}
.flex-j-sb {
.flex-j-sb {
width: 100%;
}
.goods-row {
background: #fff;
padding: 16rpx;
>.goods-col {
display: flex;
>.goods-img {
overflow: hidden;
flex: 4;
}
>.goods-detail {
flex: 7;
}
}
}
.goods-detail {
margin: 0 20rpx;
>.title {
font-size: $font-base;
color: $font-color-dark;
line-height: 1.5;
height: 86rpx;
padding: 10rpx 0 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.count-config {
padding: 5rpx 0;
color: #666;
display: flex;
font-size: 24rpx;
letter-spacing:2rpx;
padding-left: 10rpx;
.count-config-text {
font-size: 24rpx;
}
}
}
.price-box {
margin-top: 10rpx;
.goods-row {
background: #fff;
padding: 16rpx;
>.goods-col {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10rpx;
font-size: 24rpx;
color: $font-color-light;
>.price {
font-size: 26rpx;
line-height: 1;
color: $main-color;
font-weight: bold;
.price-int {
font-size: 48rpx;
}
>.goods-img {
overflow: hidden;
flex: 4;
}
>.goods-detail {
flex: 7;
}
}
}
.goods-detail {
margin: 0 20rpx;
>.title {
font-size: $font-base;
color: $font-color-dark;
line-height: 1.5;
height: 86rpx;
padding: 10rpx 0 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.count-config {
padding: 5rpx 0;
color: #666;
display: flex;
font-size: 24rpx;
letter-spacing:2rpx;
padding-left: 10rpx;
.count-config-text {
font-size: 24rpx;
}
}
}
.price-box {
margin-top: 10rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10rpx;
font-size: 24rpx;
color: $font-color-light;
>.price {
font-size: 26rpx;
line-height: 1;
color: $main-color;
font-weight: bold;
.price-int {
font-size: 48rpx;
}
}
}
</style>

View File

@@ -1,73 +1,63 @@
<template>
<div>
<div class="goods-recommend">{{title ? `--${title}-- `:''}}</div>
<view>
<view class="goods-recommend">{{title ? `--${title}-- `:''}}</view>
<goodsTemplate :res='goodsList' />
</div>
</view>
</template>
<script>
import goodsTemplate from '@/components/m-goods-list/list'
import { getGoodsList } from "@/api/goods.js";
export default {
data() {
return {
goodsList: [],
params: {
pageNumber: 1,
},
};
},
props: {
title: {
type: String,
default: "",
},
pageSize: {
type: null,
default: 12,
},
categoryId: {
type: null,
default: "",
},
storeId: {
type: null,
default: "",
},
},
components:{goodsTemplate},
mounted() {
this.initGoods();
},
methods: {
/**
* 初始化商品
*/
async initGoods() {
let submit = JSON.parse(
JSON.stringify(
Object.assign(this.params, {
pageSize: this.pageSize,
categoryId: this.categoryId,
storeId: this.storeId,
})
)
);
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import goodsTemplate from '@/components/m-goods-list/list.vue'
import { getGoodsList } from "@/api/goods.js"
Object.keys(submit).map((key) => {
if (!submit[key] || submit[key].length == 0) {
delete submit[key];
}
});
let goodsList = await getGoodsList(submit);
this.goodsList.push(...goodsList.data.result.records);
},
handleClick(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
},
};
const props = withDefaults(defineProps<{
title?: string
pageSize?: number | null
categoryId?: string | null
storeId?: string | null
}>(), {
title: '',
pageSize: 12,
categoryId: '',
storeId: ''
})
const goodsList = ref<any[]>([])
const params = ref({
pageNumber: 1,
})
onMounted(() => {
initGoods()
})
/**
* 初始化商品
*/
const initGoods = async () => {
let submit = JSON.parse(
JSON.stringify(
Object.assign(params.value, {
pageSize: props.pageSize,
categoryId: props.categoryId,
storeId: props.storeId,
})
)
)
Object.keys(submit).map((key) => {
if (!submit[key] || (submit[key] as any).length == 0) {
delete submit[key]
}
})
let res = await getGoodsList(submit)
goodsList.value.push(...(res.data.result.records as any[]))
}
const handleClick = (item: any) => {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
</script>
<style scoped lang="scss">
@@ -130,4 +120,4 @@ $w_94: 94%;
}
}
}
</style>
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="index">
<view class="index">
<view v-model="show" class="slot-content">
<image @click="downLoad()" class="img" :src="imgUrl" />
<div class="canvas-hide">
@@ -11,181 +11,153 @@
<!-- #endif -->
</div>
</view>
</div>
</view>
</template>
<script>
<script setup lang="ts">
import { ref, onMounted, getCurrentInstance } from 'vue'
// 引入绘制插件
import DrawPoster from "@/js_sdk/u-draw-poster";
import DrawPoster from "@/js_sdk/u-draw-poster"
// 生成二维码
import uQRCode from '@/components/Sansnn-uQRCode/uqrcode.js';
import logoImg from "@/pages/passport/static/logo-title.png";
import myFaceImg from "@/pages/passport/static/missing-face.png";
import uQRCode from '@/components/Sansnn-uQRCode/uqrcode.js'
import logoImg from "@/pages/passport/static/logo-title.png"
import myFaceImg from "@/pages/passport/static/missing-face.png"
export default {
data: () => ({
imgUrl: "", //绘制出来的图片路径
show: false, //是否展示模态框
dp: {}, //绘制的dp对象用于存储绘制等一些方法。
logo: logoImg, // 本地logo地址
myFace: myFaceImg, // 本地默认头像
sharingLink: '', // 二维码链接
}),
props: {
/**
* 父级传参的数据
*/
res: {
type: null,
default: "",
},
},
onUnload() {},
onReady() {},
methods: {
/** 解决微信小程序中图片模糊问题 */
// #ifdef MP-WEIXIN
st2: (size) => size * 2,
// #endif
const instance = getCurrentInstance()
// #ifndef MP-WEIXIN
st2: (size) => size,
// #endif
const props = defineProps<{
res?: any
}>()
/** 保存图片 */
downLoad() {
uni.saveImageToPhotosAlbum({
filePath: this.imgUrl,
success: function () {
uni.showToast({title: "保存成功!",icon: "none",});
},
fail: function () {
uni.showToast({title: "保存失败,请稍后重试!",icon: "none",});
},
});
},
/** 创建canvas */
async init() {
this.show = true;
this.dp = await DrawPoster.build({
selector: "canvas",
componentThis: this,
loading: true,
debugging: true,
});
let dp = this.dp;
// #ifdef MP-WEIXIN
// 用于微信小程序中画布错乱问题
dp.canvas.width = this.st2(560);
dp.canvas.height = this.st2(800);
// #endif
this.showQRCode(dp);
},
/** 生成二维码 */
async showQRCode(dp){
await uQRCode.make({
canvasId: 'canvas', // canvas画布
componentInstance: this,
text: this.res.bottom.code, // 二维码内容
size: 130, // 二维码大小
margin: 5, // 二维码内边距
backgroundColor: '#ffffff', // 二维码背景颜色
foregroundColor: '#000000', // !二维码色块颜色
fileType: 'jpg',
errorCorrectLevel: 0, // <== 关键 容错率M:0,L:1,H:2,Q:3,
// errorCorrectLevel: uQRCode.errorCorrectLevel.M, // <== 关键 容错率M:0,L:1,H:2,Q:3,
success: res => {
this.sharingLink = res; // res => 图片路径
// 对画布进行绘制
this.draw(dp);
},
fail: res => {
// console.log('失败',res)
}
const imgUrl = ref("")
const show = ref(false)
const dp = ref<any>({})
const logo = logoImg
const myFace = myFaceImg
const sharingLink = ref('')
/** 解决微信小程序中图片模糊问题 */
// #ifdef MP-WEIXIN
const st2 = (size: number) => size * 2
// #endif
// #ifndef MP-WEIXIN
const st2 = (size: number) => size
// #endif
/** 保存图片 */
const downLoad = () => {
uni.saveImageToPhotosAlbum({
filePath: imgUrl.value,
success: function () {
uni.showToast({title: "保存成功!",icon: "none"})
},
fail: function () {
uni.showToast({title: "保存失败,请稍后重试!",icon: "none"})
},
})
}
/** 创建canvas */
const init = async () => {
show.value = true
dp.value = await DrawPoster.build({
selector: "canvas",
componentThis: instance?.proxy,
loading: true,
debugging: true,
})
let dpVal = dp.value
// #ifdef MP-WEIXIN
// 用于微信小程序中画布错乱问题
dpVal.canvas.width = st2(560)
dpVal.canvas.height = st2(800)
// #endif
await showQRCode(dpVal)
}
/** 生成二维码 */
const showQRCode = async (dp: any) => {
await uQRCode.make({
canvasId: 'canvas',
componentInstance: instance?.proxy,
text: props.res.bottom.code,
size: 130,
margin: 5,
backgroundColor: '#ffffff',
foregroundColor: '#000000',
fileType: 'jpg',
errorCorrectLevel: 0,
success: (res: any) => {
sharingLink.value = res
draw(dp)
},
fail: (_res: any) => {
// console.log('失败',res)
}
})
}
const draw = async (dp: any) => {
const { face, nickName, desc } = props.res.memberInfo
const { width, height, background, title } = props.res.container
const { img, price } = props.res.bottom
/** 绘制背景 */
await dp.draw((ctx: any) => {
ctx.fillStyle = background
ctx.fillRoundRect(st2(0), st2(0), st2(width), st2(height), st2(12))
ctx.clip()
})
/** 绘制圆角矩形 */
await dp.draw(async (ctx: any) => {
ctx.fillStyle = "#ffffff"
ctx.strokeStyle = "#ffffff"
ctx.lineWidth = st2(1)
ctx.fillRoundRect(30, 150, 500, 620, 0)
})
/** 绘制图片 */
dp.draw(async (ctx: any) => {
await Promise.all([
ctx.drawImage(face ? face : myFace, st2(30), st2(30), st2(90), st2(90)),
ctx.drawImage(img, st2(60), st2(170), st2(440), st2(440)),
ctx.drawImage(sharingLink.value, st2(375), st2(625), st2(130), st2(130))
])
})
/** 绘制顶部文字(昵称 简述) */
await dp.draw((ctx: any) => {
ctx.fillStyle = "#666"
ctx.font = `${st2(24)}px PingFang SC`
ctx.fillText(nickName, st2(150), st2(65))
ctx.fillStyle = "#666"
ctx.font = `${st2(24)}px PingFang SC`
ctx.fillText(desc, st2(150), st2(105))
})
/** 绘制中间文字(商品名称 价格) */
await dp.draw((ctx: any) => {
ctx.fillStyle = "#333"
ctx.font = `bold ${st2(24)}px PingFang SC`
ctx.textAlign = "left"
ctx.fillWarpText({
text: title,
lineHeight: st2(32),
maxWidth: st2(280),
x: st2(60),
y: st2(710),
layer: 2,
})
},
async draw(dp) {
const { face, nickName, desc } = this.res.memberInfo;
const { width, height, background, title } = this.res.container;
const { code, img, price } = this.res.bottom;
ctx.fillStyle = "#ff3c2a"
ctx.font = `${st2(38)}px PingFang SC`
ctx.textAlign = "left"
ctx.fillText(price, st2(60), st2(665))
})
/** 绘制背景 */
await dp.draw((ctx) => {
ctx.fillStyle = background;
ctx.fillRoundRect( this.st2(0), this.st2(0), this.st2(width), this.st2(height), this.st2(12));
ctx.clip();
});
/** 绘制圆角矩形 */
await dp.draw(async (ctx)=>{
// 设置矩形色彩
ctx.fillStyle = "#ffffff";
// 设置图形轮廓的颜色。默认情况下线条和填充颜色都是黑色CSS 颜色值 #000000
ctx.strokeStyle = "#ffffff";
// 这个属性设置当前绘线的粗细。属性值必须为正数。描述线段宽度的数字。 0、 负数、 Infinity 和 NaN 会被忽略。默认值是1.0。
ctx.lineWidth = this.st2(1);
// 进行绘制
ctx.fillRoundRect(30, 150, 500, 620, 0);
// ctx.strokeRoundRect(30, 150, 500, 620, 0);
// ctx.strokeRect(30, 150, 500, 620, 0);
})
/** 绘制图片 */
dp.draw(async (ctx) => {
await Promise.all([
// 绘制头像face
ctx.drawImage(face?face:this.myFace, this.st2(30), this.st2(30), this.st2(90), this.st2(90)),
// 绘制Logo
// ctx.drawImage( this.logo, this.st2(175), this.st2(0), this.st2(256), this.st2(144)),
// 中间图片
ctx.drawImage(img, this.st2(60), this.st2(170), this.st2(440), this.st2(440)),
// 二维码
ctx.drawImage(this.sharingLink, this.st2(375), this.st2(625), this.st2(130), this.st2(130))
]);
});
/** 绘制顶部文字(昵称 简述) */
await dp.draw((ctx) => {
ctx.fillStyle = "#666";
ctx.font = `${this.st2(24)}px PingFang SC`;
ctx.fillText(nickName, this.st2(150), this.st2(65));
ctx.fillStyle = "#666";
ctx.font = `${this.st2(24)}px PingFang SC`;
ctx.fillText(desc, this.st2(150), this.st2(105));
});
/** 绘制中间文字(商品名称 价格) */
await dp.draw((ctx) => {
ctx.fillStyle = "#333";
ctx.font = `bold ${this.st2(24)}px PingFang SC`;
ctx.textAlign = "left";
ctx.fillWarpText({
text: title,
lineHeight: this.st2(32),
maxWidth: this.st2(280),
x: this.st2(60),
y: this.st2(710),
layer: 2,
});
ctx.fillStyle = "#ff3c2a";
ctx.font = `${this.st2(38)}px PingFang SC`;
ctx.textAlign = "left";
ctx.fillText(price, this.st2(60), this.st2(665));
});
/** 绘制底部文字 */
// await dp.draw((ctx) => {
// ctx.fillStyle = "#666";
// ctx.font = `${this.st2(24)}px PingFang SC`;
// ctx.fillText("长按图片,识别二维码", this.st2(200), this.st2(866));
// ctx.fillStyle = "#666";
// ctx.font = `${this.st2(24)}px PingFang SC`;
// ctx.fillText("查看商品详情", this.st2(200), this.st2(900));
// });
// 绘制生成本地地址
this.imgUrl = await dp.createImagePath();
},
},
// 绘制生成本地地址
imgUrl.value = await dp.createImagePath()
}
async mounted() {
this.init();
},
};
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>

View File

@@ -1,17 +1,23 @@
<template>
<view class="serach">
<view class="left-box" @tap="onClickLeft">
<u-icon name="arrow-left" size="28" color="#666"></u-icon>
</view>
<view class="content" :style="{ 'border-radius': radius + 'px' }">
<!-- HM修改 增加进入输入状态的点击范围 -->
<view class="content-box" :class="{ center: mode === 2 }">
<u-icon name="search" size="32" style="padding:0 15rpx;"></u-icon>
<view class="serach">
<view class="left-box" @tap="onClickLeft">
<u-icon name="arrow-left" size="28rpx" color="#606266"></u-icon>
</view>
<view class="content" :style="{ 'border-radius': radius + 'px' }">
<!-- HM修改 增加进入输入状态的点击范围 -->
<view class="content-box" :class="{ center: mode === 2 }">
<u-icon name="search" size="30rpx" color="#909399" style="padding:0 14rpx;"></u-icon>
<!-- HM修改 增加placeholder input confirm-type confirm-->
<input style="width:100%; " :placeholder="placeholder" placeholder-class="placeholder-color"
@input="inputChange" confirm-type="search" @confirm="triggerConfirm" class="input"
:class="{ center: !active && mode === 2 }" :focus="isFocus" v-model="inputVal" @focus="focus" @blur="blur" />
<u-icon name="close" v-if="showClear && isDelShow" style="padding:0 30rpx;" @click="clear"></u-icon>
<view
class="clear-icon"
v-if="showClear && isDelShow"
@tap.stop.prevent="clear"
>
<u-icon name="close" size="28rpx" color="#909399"></u-icon>
</view>
</view>
</view>
@@ -20,147 +26,156 @@
<div @click="out()">取消</div>
</view>
<view v-else class="button-item">
<u-icon name="grid-fill" size="32" @click="handelListClass()" v-if="!switchLayout"></u-icon>
<u-icon v-else @click="handelListClass()" name="list-dot" size="32"></u-icon>
</view>
<view v-else class="button-item">
<u-icon name="grid-fill" size="32rpx" color="var(--theme-light, #ff6b35)" @click="handelListClass()" v-if="!switchLayout"></u-icon>
<u-icon v-else @click="handelListClass()" name="list-dot" size="32rpx" color="var(--theme-light, #ff6b35)"></u-icon>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
mode: {
value: Number,
default: 1,
},
//HM修改 定义默认搜索关键词(水印文字)
placeholder: {
value: String,
default: "请输入搜索内容",
},
value: {
type: String,
default: "",
},
// 默认半径为60
radius: {
value: String,
default: 60,
},
// 是否获取焦点
isFocusVal: {
value: Boolean,
default: true,
},
showClear: {
type: Boolean,
default: true,
},
},
data() {
return {
isShowSeachGoods: false, //是否显示查询的商品
active: false, //是否选中
inputVal: "", //Input中内容
isDelShow: false, //是否显示右侧删除icon
isFocus: false, //是否获取焦点
switchLayout: true, //切换当前商品的布局,默认为两列
};
},
mounted() {
this.isFocus = this.isFocusVal;
},
methods: {
//
out() {
uni.reLaunch({
url: "/pages/tabbar/home/index",
});
},
// 切换排列顺序
handelListClass() {
this.switchLayout = !this.switchLayout;
this.$emit("SwitchType");
},
//HM修改 触发组件confirm事件
triggerConfirm() {
this.$emit("confirm", false);
uni.hideKeyboard();
},
//HM修改 触发组件input事件
inputChange(event) {
var keyword = event.detail.value;
this.$emit("input", keyword);
if (this.inputVal) {
this.isDelShow = true;
}
},
focus() {
this.active = true;
//HM修改 增加获取焦点判断
if (this.inputVal) {
this.isDelShow = true;
}
},
blur() {
this.isFocus = false;
if (!this.inputVal) {
this.active = false;
}
},
clear() {
//HM修改 收起键盘
uni.hideKeyboard();
this.isFocus = false;
this.inputVal = "";
this.active = false;
//HM修改 清空内容时候触发组件input
this.$emit("input", "");
//this.$emit('search', '');//HM修改 清空内容时候不进行搜索
},
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
/**
* 回退到上一级
*/
onClickLeft() {
const paths = getCurrentPages();
console.log(paths)
if(paths.length > 1){
uni.navigateBack();
}else{
uni.switchTab({
url:"/pages/tabbar/home/index"
})
}
},
const props = withDefaults(defineProps<{
mode?: number
placeholder?: string
modelValue?: string
value?: string
radius?: string
isFocusVal?: boolean
showClear?: boolean
}>(), {
mode: 1,
placeholder: "请输入搜索内容",
modelValue: "",
value: "",
radius: "60",
isFocusVal: true,
showClear: true,
})
const emit = defineEmits(['confirm', 'input', 'update:modelValue', 'search', 'SwitchType'])
/**
* 内容为空时,输入默认关键字
*/
search() {
if (!this.inputVal) {
if (this.searchName == "取消") {
uni.hideKeyboard();
this.isFocus = false;
this.active = false;
return;
}
}
this.$emit("search", this.inputVal ? this.inputVal : this.placeholder);
},
},
watch: {
/**
* 监听当前是否有值 是否显示清除图标
*/
inputVal(newVal) {
newVal ? (this.isDelShow = true) : (this.isDelShow = false);
},
},
};
</script>
const isShowSeachGoods = ref(false)
const active = ref(false)
const inputVal = ref("")
const isDelShow = ref(false)
const isFocus = ref(false)
const switchLayout = ref(true)
onMounted(() => {
isFocus.value = props.isFocusVal
inputVal.value = props.modelValue || props.value || ''
})
//
const out = () => {
uni.reLaunch({
url: "/pages/tabbar/home/index",
})
}
// 切换排列顺序
const handelListClass = () => {
switchLayout.value = !switchLayout.value
emit("SwitchType")
}
//HM修改 触发组件confirm事件
const triggerConfirm = () => {
emit("confirm", false)
uni.hideKeyboard()
}
//HM修改 触发组件input事件
const inputChange = (event: any) => {
var keyword = event.detail.value
emit("input", keyword)
emit("update:modelValue", keyword)
if (inputVal.value) {
isDelShow.value = true
}
}
const focus = () => {
active.value = true
//HM修改 增加获取焦点判断
if (inputVal.value) {
isDelShow.value = true
}
}
const blur = () => {
isFocus.value = false
if (!inputVal.value) {
active.value = false
}
}
const clear = () => {
//HM修改 收起键盘
uni.hideKeyboard()
isFocus.value = false
inputVal.value = ""
active.value = false
isDelShow.value = false
//HM修改 清空内容时候触发组件input
emit("input", "")
emit("update:modelValue", "")
//this.$emit('search', '');//HM修改 清空内容时候不进行搜索
}
const setInputValue = (value: string) => {
inputVal.value = value || ''
emit("input", inputVal.value)
emit("update:modelValue", inputVal.value)
}
/**
* 回退到上一级
*/
const onClickLeft = () => {
const paths = getCurrentPages()
console.log(paths)
if(paths.length > 1){
uni.navigateBack()
}else{
uni.switchTab({
url:"/pages/tabbar/home/index"
})
}
}
/**
* 内容为空时,输入默认关键字
*/
const search = () => {
if (!inputVal.value) {
if (false) { // searchName == "取消" - 兼容旧逻辑
uni.hideKeyboard()
isFocus.value = false
active.value = false
return
}
}
emit("search", inputVal.value ? inputVal.value : props.placeholder)
}
/**
* 监听当前是否有值 是否显示清除图标
*/
watch(inputVal, (newVal) => {
newVal ? (isDelShow.value = true) : (isDelShow.value = false)
})
watch(() => props.modelValue, (newVal) => {
if (newVal !== inputVal.value) {
inputVal.value = newVal || ''
}
})
defineExpose({
isShowSeachGoods,
inputVal,
clear,
setInputValue,
})
</script>
<style lang="scss" scoped>
.serach {
@@ -199,20 +214,29 @@ export default {
justify-content: flex-start;
}
.input {
flex: 1;
min-width: 0;
width: auto;
.input {
flex: 1;
min-width: 0;
width: auto;
line-height: 60rpx;
height: 60rpx;
transition: all 0.2s linear;
&.center {
width: auto;
}
}
}
}
}
}
.clear-icon {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 72rpx;
height: 70rpx;
}
}
}
.button {
display: flex;

View File

@@ -28,118 +28,125 @@
</view>
</u-popup>
</template>
<script>
import { h5Copy } from "@/js_sdk/h5-copy/h5-copy.js";
import configs from "@/config/config";
import mpShare from "@/utils/mpShare.js";
import { setClipboard } from "@/utils/filters.js";
export default {
mixins: [mpShare],
data() {
return {
configs,
show: true,
list: [
{
color: "#04BE02",
title: "微信好友",
icon: "weixin-fill",
type: 0,
},
{
color: "#04BE02",
title: "朋友圈",
icon: "weixin-circle-fill",
type: 1,
},
],
};
<script setup lang="ts">
import { ref } from 'vue'
import configs from "@/config/config"
import mpShare from "@/utils/mpShare.js"
import { setClipboard } from "@/utils/filters.js"
// mixin 处理mpShare 提供小程序分享能力
mpShare
const props = defineProps<{
thumbnail?: string
goodsName?: string
type?: string
goodsId?: string | number
link?: string
}>()
const emit = defineEmits(['close'])
const show = ref(true)
const config = configs
const list = ref([
{
color: "#04BE02",
title: "微信好友",
icon: "weixin-fill",
type: 0,
},
// 图片缩略图、 商品名称 、 typegoods,shop,pintuan) 拼团商品分享以及店铺分享
props: ["thumbnail", "goodsName", "type", "goodsId", "link"],
methods: {
close() {
this.$emit("close");
},
weChatShare(){
this.$u.mpShare = {
title: this.shareTitle(), // 默认为小程序名称,可自定义
path: '', // 默认为当前页面路径一般无需修改QQ小程序不支持
// 分享图标,路径可以是本地文件路径、代码包文件路径或者网络图片路径。
// 支持PNG及JPG默认为当前页面的截图
imageUrl: this.thumbnail ||''
}
},
// h5复制链接
// #ifdef H5
copyLink() {
let content;
if (this.link) {
content = this.configs.shareLink + this.link;
} else {
content =
this.configs.shareLink +
getCurrentPages()[getCurrentPages().length - 1].__page__.fullPath;
}
setClipboard(content)
},
// #endif
shareTitle() {
let shareTitle;
if (this.type == "goods") {
shareTitle = `[好友推荐]${this.goodsName}快来跟我一起看看吧`;
} else if (this.type == "shops") {
shareTitle = `[好友发现]${this.goodsName}快来跟我一起看看吧`;
} else if (this.type == "pintuan") {
shareTitle = `[好友邀请]${this.goodsName}快来跟我一起抢购吧!`;
} else if (this.type == "kanjia") {
shareTitle = `[好友邀请]请快来帮我砍一刀${this.goodsName}`;
}
return shareTitle;
},
// #ifdef APP-PLUS
handleShare(val) {
console.log("12312312")
if (val.type <= 1) {
let scene; // "WXSenceTimeline 朋友圈 WXSceneSession 微信好友"
val.type == 1
? (scene = "WXSenceTimeline")
: (scene = "WXSceneSession");
uni.share({
provider: "weixin",
scene: scene,
href: configs.shareLink + this.link,
imageUrl: this.thumbnail,
type: 0,
summary: this.goodsName,
title: this.shareTitle(),
success: function (res) {
uni.showToast({
title: "分享成功!",
duration: 2000,
icon: "none",
});
this.$emit("close");
},
fail: function (err) {
uni.showToast({
title: "分享失败!",
duration: 2000,
icon: "none",
});
this.$emit("close");
},
});
}
},
// #endif
{
color: "#04BE02",
title: "朋友圈",
icon: "weixin-circle-fill",
type: 1,
},
};
])
const close = () => {
emit("close")
}
const weChatShare = () => {
// @ts-ignore
const proxy = getCurrentInstance()?.proxy
if (proxy) {
// @ts-ignore
proxy.$u.mpShare = {
title: shareTitle(),
path: '',
imageUrl: props.thumbnail || ''
}
}
}
// h5复制链接
// #ifdef H5
const copyLink = () => {
let content: string
if (props.link) {
content = config.shareLink + props.link
} else {
content =
config.shareLink +
getCurrentPages()[getCurrentPages().length - 1].__page__.fullPath
}
setClipboard(content)
}
// #endif
const shareTitle = (): string => {
let shareTitle: string = ''
if (props.type == "goods") {
shareTitle = `[好友推荐]${props.goodsName}快来跟我一起看看吧`
} else if (props.type == "shops") {
shareTitle = `[好友发现]${props.goodsName}快来跟我一起看看吧`
} else if (props.type == "pintuan") {
shareTitle = `[好友邀请]${props.goodsName}快来跟我一起抢购吧!`
} else if (props.type == "kanjia") {
shareTitle = `[好友邀请]请快来帮我砍一刀${props.goodsName}`
}
return shareTitle
}
// #ifdef APP-PLUS
const handleShare = (val: any) => {
console.log("12312312")
if (val.type <= 1) {
let scene: string // "WXSenceTimeline 朋友圈 WXSceneSession 微信好友"
val.type == 1
? (scene = "WXSenceTimeline")
: (scene = "WXSceneSession")
uni.share({
provider: "weixin",
scene: scene,
href: config.shareLink + props.link,
imageUrl: props.thumbnail,
type: 0,
summary: props.goodsName,
title: shareTitle(),
success: function () {
uni.showToast({
title: "分享成功!",
duration: 2000,
icon: "none",
})
emit("close")
},
fail: function () {
uni.showToast({
title: "分享失败!",
duration: 2000,
icon: "none",
})
emit("close")
},
})
}
}
// #endif
</script>
<style lang="scss" scoped>
@import "./mp-share.scss";
@@ -175,4 +182,4 @@ export default {
}
}
}
</style>
</style>

View File

@@ -1,27 +1,21 @@
<template>
<div>
<view>
<u-popup v-model:show="show" mode="bottom" height="800rpx" border-radius="14">
<div class="wrapper">
<view class="wrapper">
<view class="down-goods-tips">该商品已下架</view>
<scroll-view scroll-y="true" style="height: 670rpx">
<goodsRecommend title="其他商品" />
</scroll-view>
</div>
</view>
</u-popup>
</div>
</view>
</template>
<script>
import goodsRecommend from "@/components/m-goods-recommend/index.vue";
<script setup lang="ts">
import { ref } from 'vue'
import goodsRecommend from "@/components/m-goods-recommend/index.vue"
export default {
data() {
return {
show: true, // 是否显示
};
},
components: { goodsRecommend },
};
const show = ref(true)
</script>
<style lang="scss" scoped>

View File

@@ -2,212 +2,192 @@
<view class="shadow" :class="!show?'':'shadow-show'" :style="{backgroundColor:show?maskBg:'rgba(0,0,0,0)'}" @tap="tapMask">
<view class="popups" :class="[theme]" :style="{top: popupsTop ,left: popupsLeft,flexDirection:direction}">
<text :class="dynPlace" :style="{width:'0px',height:'0px'}" v-if="triangle"></text>
<view v-for="(item,index) in popData" :key="index" @tap.stop="tapItem(item)" class="itemChild view" :class="[direction=='row'?'solid-right':'solid-bottom',item.disabled?'disabledColor':'']">
<u-icon size="35" :name="item.icon" v-if="item.icon"></u-icon><span class="title">{{item.title}}</span>
<view v-for="(item,index) in popData" :key="index" @tap.stop="tapItem(item)" class="itemChild view" :class="[direction=='row'?'solid-right':'solid-bottom',item.disabled?'disabledColor':'']">
<view class="popup-icon" v-if="item.icon">
<u-icon size="35rpx" :name="item.icon"></u-icon>
</view>
<span class="title">{{item.title}}</span>
</view>
<slot></slot>
</view>
</view>
</template>
<script>
export default {
props: {
maskBg: {
type: String,
default: "rgba(0,0,0,0)",
},
placement: {
type: String,
default: "default", //default top-start top-end bottom-start bottom-end
},
direction: {
type: String,
default: "column", //column row
},
x: {
type: Number,
default: 0,
},
y: {
type: Number,
default: 0,
},
value: {
type: Boolean,
default: false,
},
popData: {
type: Array,
default: () => [],
},
theme: {
type: String,
default: "light", //light dark
},
dynamic: {
type: Boolean,
default: false,
},
gap: {
type: Number,
default: 20,
},
triangle: {
type: Boolean,
default: true,
},
},
data() {
return {
popupsTop: "0rpx",
popupsLeft: "0rpx",
show: false,
dynPlace: "",
};
},
mounted() {
this.popupsPosition();
},
methods: {
tapMask() {
this.$emit("input", !this.value);
},
tapItem(item) {
if (item.disabled) return;
this.$emit("tapPopup", item);
this.$emit("input", !this.value);
},
getStatusBar() {
const windowInfo = uni.getWindowInfo();
// #ifdef H5
return Promise.resolve(
(windowInfo.statusBarHeight || 0) + (windowInfo.windowTop || 0)
);
// #endif
// #ifndef H5
return Promise.resolve(windowInfo.statusBarHeight || 0);
// #endif
},
async popupsPosition() {
let statusBar = await this.getStatusBar();
let promise = new Promise((resolve, reject) => {
let popupsDom = uni.createSelectorQuery().in(this).select(".popups");
popupsDom
.fields(
{
size: true,
},
(data) => {
if (!data) {
resolve();
return;
}
let width = data.width;
let height = data.height;
<script setup lang="ts">
import { ref, watch, onMounted, getCurrentInstance } from 'vue'
const props = withDefaults(defineProps<{
maskBg?: string
placement?: string
direction?: string
x?: number
y?: number
modelValue?: boolean
popData?: any[]
theme?: string
dynamic?: boolean
gap?: number
triangle?: boolean
}>(), {
maskBg: "rgba(0,0,0,0)",
placement: "default",
direction: "column",
x: 0,
y: 0,
modelValue: false,
popData: () => [],
theme: "light",
dynamic: false,
gap: 20,
triangle: true,
})
let y = this.dynamic
? this.dynamicGetY(this.y, this.gap)
: this.transformRpx(this.y);
const emit = defineEmits(['update:modelValue', 'tapPopup'])
let x = this.dynamic
? this.dynamicGetX(this.x, this.gap)
: this.transformRpx(this.x);
const instance = getCurrentInstance()
// #ifdef H5
y = this.dynamic
? this.y + statusBar
: this.transformRpx(this.y + statusBar);
// #endif
const popupsTop = ref("0rpx")
const popupsLeft = ref("0rpx")
const show = ref(false)
const dynPlace = ref("")
this.dynPlace =
this.placement == "default"
? this.getPlacement(x, y)
: this.placement;
onMounted(() => {
popupsPosition()
})
switch (this.dynPlace) {
case "top-start":
this.popupsTop = `${y + 9}rpx`;
this.popupsLeft = `${x - 15}rpx`;
break;
case "top-end":
this.popupsTop = `${y + 9}rpx`;
this.popupsLeft = `${x + 15 - width}rpx`;
break;
case "bottom-start":
this.popupsTop = `${y - 18 - height}rpx`;
this.popupsLeft = `${x - 15}rpx`;
break;
case "bottom-end":
this.popupsTop = `${y - 9 - height}rpx`;
this.popupsLeft = `${x + 15 - width}rpx`;
break;
}
resolve();
}
)
.exec();
});
return promise;
},
getPlacement(x, y) {
const { windowWidth: width, windowHeight: height } = uni.getWindowInfo();
if (x > width / 2 && y > height / 2) {
return "bottom-end";
} else if (x < width / 2 && y < height / 2) {
return "top-start";
} else if (x > width / 2 && y < height / 2) {
return "top-end";
} else if (x < width / 2 && y > height / 2) {
return "bottom-start";
} else if (x > width / 2) {
return "top-end";
} else {
return "top-start";
}
},
dynamicGetY(y, gap) {
const { windowHeight: height } = uni.getWindowInfo();
y = y < gap ? gap : y;
y = height - y < gap ? height - gap : y;
const tapMask = () => {
emit("update:modelValue", !props.modelValue)
}
return y;
},
dynamicGetX(x, gap) {
const { windowWidth: width } = uni.getWindowInfo();
x = x < gap ? gap : x;
x = width - x < gap ? width - gap : x;
return x;
},
transformRpx(params) {
const { screenWidth } = uni.getWindowInfo();
return (params * screenWidth) / 375;
},
},
watch: {
value: {
immediate: true,
handler: async function (newVal, oldVal) {
if (newVal) await this.popupsPosition();
this.show = newVal;
},
},
placement: {
immediate: true,
handler(newVal, oldVal) {
this.dynPlace = newVal;
},
},
},
};
const tapItem = (item: any) => {
if (item.disabled) return
emit("tapPopup", item)
emit("update:modelValue", !props.modelValue)
}
const getStatusBar = () => {
const windowInfo = uni.getWindowInfo()
// #ifdef H5
return Promise.resolve(
(windowInfo.statusBarHeight || 0) + (windowInfo.windowTop || 0)
)
// #endif
// #ifndef H5
return Promise.resolve(windowInfo.statusBarHeight || 0)
// #endif
}
const popupsPosition = async () => {
let statusBar = await getStatusBar()
await new Promise<void>((resolve) => {
let popupsDom = uni.createSelectorQuery().in(instance!.proxy).select(".popups")
popupsDom
.fields(
{
size: true,
},
(data: any) => {
if (!data) {
resolve()
return
}
let width = data.width
let height = data.height
let y = props.dynamic
? dynamicGetY(props.y, props.gap)
: transformRpx(props.y)
let x = props.dynamic
? dynamicGetX(props.x, props.gap)
: transformRpx(props.x)
// #ifdef H5
y = props.dynamic
? props.y + statusBar
: transformRpx(props.y + statusBar)
// #endif
dynPlace.value =
props.placement == "default"
? getPlacement(x, y)
: props.placement
switch (dynPlace.value) {
case "top-start":
popupsTop.value = `${y + 9}rpx`
popupsLeft.value = `${x - 15}rpx`
break
case "top-end":
popupsTop.value = `${y + 9}rpx`
popupsLeft.value = `${x + 15 - width}rpx`
break
case "bottom-start":
popupsTop.value = `${y - 18 - height}rpx`
popupsLeft.value = `${x - 15}rpx`
break
case "bottom-end":
popupsTop.value = `${y - 9 - height}rpx`
popupsLeft.value = `${x + 15 - width}rpx`
break
}
resolve()
}
)
.exec()
})
}
const getPlacement = (x: number, y: number) => {
const { windowWidth: width, windowHeight: height } = uni.getWindowInfo()
if (x > width / 2 && y > height / 2) {
return "bottom-end"
} else if (x < width / 2 && y < height / 2) {
return "top-start"
} else if (x > width / 2 && y < height / 2) {
return "top-end"
} else if (x < width / 2 && y > height / 2) {
return "bottom-start"
} else if (x > width / 2) {
return "top-end"
} else {
return "top-start"
}
}
const dynamicGetY = (y: number, gap: number) => {
const { windowHeight: height } = uni.getWindowInfo()
y = y < gap ? gap : y
y = height - y < gap ? height - gap : y
return y
}
const dynamicGetX = (x: number, gap: number) => {
const { windowWidth: width } = uni.getWindowInfo()
x = x < gap ? gap : x
x = width - x < gap ? width - gap : x
return x
}
const transformRpx = (params: number) => {
const { screenWidth } = uni.getWindowInfo()
return (params * screenWidth) / 375
}
watch(() => props.modelValue, async (newVal) => {
if (newVal) await popupsPosition()
show.value = newVal
}, { immediate: true })
watch(() => props.placement, (newVal) => {
dynPlace.value = newVal
}, { immediate: true })
</script>
<style lang="scss" scoped>
.title {
margin-left: 20rpx;
}
.title {
margin-left: 16rpx;
}
.shadow {
position: fixed;
top: 0;
@@ -229,13 +209,22 @@ export default {
padding: 20rpx;
border-radius: 5px;
display: flex;
.view {
display: flex;
align-items: center;
padding: 15rpx 10rpx;
font-size: 25rpx;
}
.image {
.view {
display: flex;
align-items: center;
padding: 15rpx 10rpx;
font-size: 25rpx;
}
.popup-icon {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
}
.image {
display: inline-block;
vertical-align: middle;
width: 40rpx;

View File

@@ -11,29 +11,21 @@
</view>
</template>
<script>
export default {
name: 'UTimeLineItem',
props: {
nodeTop: {
type: [String, Number],
default: ''
},
bgColor: {
type: String,
default: '#ffffff'
}
},
computed: {
nodeStyle() {
if (this.nodeTop === '' || this.nodeTop === null || this.nodeTop === undefined) {
return {}
}
const top = typeof this.nodeTop === 'number' ? `${this.nodeTop}rpx` : this.nodeTop
return { top }
}
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
nodeTop?: string | number
bgColor?: string
}>()
const nodeStyle = computed(() => {
if (props.nodeTop === '' || props.nodeTop === null || props.nodeTop === undefined) {
return {}
}
}
const top = typeof props.nodeTop === 'number' ? `${props.nodeTop}rpx` : props.nodeTop
return { top }
})
</script>
<style lang="scss" scoped>

View File

@@ -4,10 +4,8 @@
</view>
</template>
<script>
export default {
name: 'UTimeLine'
}
<script setup lang="ts">
// UTimeLine 组件
</script>
<style lang="scss" scoped>

View File

@@ -26,42 +26,28 @@
</view>
</template>
<script>
export default {
name: 'uni-load-more',
props: {
status: {
//上拉的状态more-loading前loading-loading中noMore-没有更多了
type: String,
default: 'more'
},
showIcon: {
type: Boolean,
default: true
},
showText: {
type: Boolean,
default: true
},
color: {
type: String,
default: '#777777'
},
contentText: {
type: Object,
default() {
return {
contentdown: '上拉显示更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
};
}
}
},
data() {
return {};
<script setup lang="ts">
withDefaults(defineProps<{
status?: string
showIcon?: boolean
showText?: boolean
color?: string
contentText?: {
contentdown: string
contentrefresh: string
contentnomore: string
}
};
}>(), {
status: 'more',
showIcon: true,
showText: true,
color: '#777777',
contentText: () => ({
contentdown: '上拉显示更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了',
}),
})
</script>
<style>

View File

@@ -10,211 +10,183 @@
</view>
</view>
</template>
<script>
/**
* NumberBox 数字输入框
* @description 带加减按钮的数字输入框
* @tutorial https://ext.dcloud.net.cn/plugin?id=31
* @property {Number} value 输入框当前值
* @property {Number} min 最小值
* @property {Number} max 最大值
* @property {Number} step 每次点击改变的间隔大小
* @property {String} background 背景色
* @property {String} color 字体颜色(前景色)
* @property {Boolean} disabled = [true|false] 是否为禁用状态
* @event {Function} change 输入框值改变时触发的事件,参数为输入框当前的 value
* @event {Function} focus 输入框聚焦时触发的事件,参数为 event 对象
* @event {Function} blur 输入框失焦时触发的事件,参数为 event 对象
*/
export default {
name: "UniNumberBox",
emits: ['change', 'input', 'update:modelValue', 'blur', 'focus'],
props: {
value: {
type: [Number, String],
default: 1
},
modelValue: {
type: [Number, String],
default: 1
},
min: {
type: Number,
default: 0
},
max: {
type: Number,
default: 100
},
step: {
type: Number,
default: 1
},
background: {
type: String,
default: '#f5f5f5'
},
color: {
type: String,
default: '#333'
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
inputValue: 0
};
},
watch: {
value(val) {
this.inputValue = +val;
},
modelValue(val) {
this.inputValue = +val;
}
},
created() {
if (this.value === 1) {
this.inputValue = +this.modelValue;
}
if (this.modelValue === 1) {
this.inputValue = +this.value;
}
},
methods: {
_calcValue(type) {
if (this.disabled) {
return;
}
const scale = this._getDecimalScale();
let value = this.inputValue * scale;
let step = this.step * scale;
if (type === "minus") {
value -= step;
if (value < (this.min * scale)) {
return;
}
if (value > (this.max * scale)) {
value = this.max * scale
}
}
<script setup lang="ts">
import { ref, watch } from 'vue'
if (type === "plus") {
value += step;
if (value > (this.max * scale)) {
return;
}
if (value < (this.min * scale)) {
value = this.min * scale
}
}
const props = withDefaults(defineProps<{
value?: number | string
modelValue?: number | string
min?: number
max?: number
step?: number
background?: string
color?: string
disabled?: boolean
}>(), {
value: 1,
modelValue: 1,
min: 0,
max: 100,
step: 1,
background: '#f5f5f5',
color: '#333',
disabled: false,
})
this.inputValue = (value / scale).toFixed(String(scale).length - 1);
this.$emit("change", +this.inputValue);
// TODO vue2 兼容
this.$emit("input", +this.inputValue);
// TODO vue3 兼容
this.$emit("update:modelValue", +this.inputValue);
},
_getDecimalScale() {
const emit = defineEmits<{
change: [value: number]
input: [value: number]
'update:modelValue': [value: number]
blur: [event: Event]
focus: [event: Event]
}>()
let scale = 1;
// 浮点型
if (~~this.step !== this.step) {
scale = Math.pow(10, String(this.step).split(".")[1].length);
}
return scale;
},
_onBlur(event) {
this.$emit('blur', event)
let value = event.detail.value;
if (!value) {
// this.inputValue = 0;
return;
}
value = +value;
if (value > this.max) {
value = this.max;
} else if (value < this.min) {
value = this.min;
}
const scale = this._getDecimalScale();
this.inputValue = value.toFixed(String(scale).length - 1);
this.$emit("change", +this.inputValue);
this.$emit("input", +this.inputValue);
},
_onFocus(event) {
this.$emit('focus', event)
}
const inputValue = ref(0)
watch(() => props.value, (val) => {
inputValue.value = +val
})
watch(() => props.modelValue, (val) => {
inputValue.value = +val
})
if (props.value === 1) {
inputValue.value = +props.modelValue
}
if (props.modelValue === 1) {
inputValue.value = +props.value
}
function _getDecimalScale() {
let scale = 1
if (~~props.step !== props.step) {
scale = Math.pow(10, String(props.step).split('.')[1].length)
}
return scale
}
function _emitValue() {
const val = +inputValue.value
emit('change', val)
emit('input', val)
emit('update:modelValue', val)
}
function _calcValue(type: 'minus' | 'plus') {
if (props.disabled) {
return
}
const scale = _getDecimalScale()
let value = inputValue.value * scale
const step = props.step * scale
if (type === 'minus') {
value -= step
if (value < props.min * scale) {
return
}
};
if (value > props.max * scale) {
value = props.max * scale
}
}
if (type === 'plus') {
value += step
if (value > props.max * scale) {
return
}
if (value < props.min * scale) {
value = props.min * scale
}
}
inputValue.value = Number((value / scale).toFixed(String(scale).length - 1))
_emitValue()
}
function _onBlur(event: any) {
emit('blur', event)
let value = event.detail.value
if (!value) {
return
}
value = +value
if (value > props.max) {
value = props.max
} else if (value < props.min) {
value = props.min
}
const scale = _getDecimalScale()
inputValue.value = Number(value.toFixed(String(scale).length - 1))
_emitValue()
}
function _onFocus(event: Event) {
emit('focus', event)
}
</script>
<style lang="scss" scoped>
$box-height: 48rpx;
$bg: #f5f5f5;
$br: 4rpx;
$color: #333;
$box-height: 48rpx;
$bg: #f5f5f5;
$br: 4rpx;
$color: #333;
.uni-numbox {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-numbox {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-numbox-btns {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
padding: 0 8px;
background-color: $bg;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-numbox-btns {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
padding: 0 8px;
background-color: $bg;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-numbox__value {
margin: 0 4rpx;
background-color: $bg;
width: 70rpx;
height: $box-height;
text-align: center;
font-size: 24rpx;
border-left-width: 0;
border-right-width: 0;
color: $color;
}
.uni-numbox__value {
margin: 0 4rpx;
background-color: $bg;
width: 70rpx;
height: $box-height;
text-align: center;
font-size: 24rpx;
border-left-width: 0;
border-right-width: 0;
color: $color;
}
.uni-numbox__minus {
border-top-left-radius: $br;
border-bottom-left-radius: $br;
}
.uni-numbox__minus {
border-top-left-radius: $br;
border-bottom-left-radius: $br;
}
.uni-numbox__plus {
border-top-right-radius: $br;
border-bottom-right-radius: $br;
}
.uni-numbox__plus {
border-top-right-radius: $br;
border-bottom-right-radius: $br;
}
.uni-numbox--text {
// fix nvue
line-height: 40rpx;
.uni-numbox--text {
line-height: 40rpx;
font-size: 40rpx;
font-weight: 300;
color: $color;
}
font-size: 40rpx;
font-weight: 300;
color: $color;
}
.uni-numbox .uni-numbox--disabled {
color: #c0c0c0 !important;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.uni-numbox .uni-numbox--disabled {
color: #c0c0c0 !important;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
</style>

View File

@@ -1,111 +1,99 @@
<template></template>
<script>
import { getAddressCode } from "@/api/address";
<script setup lang="ts">
import { onMounted } from 'vue'
import { getAddressCode } from '@/api/address'
import config from '@/config/config'
export default {
data() {
return {
config
};
},
mounted() {
this.init();
},
methods: {
// 初始化地图并且调用
initMap() {
let that = this;
uni.chooseLocation({
success: function (res) {
/**获取地址详情地址 */
that.posToCity(res.latitude, res.longitude).then((val) => {
/**获取地址code */
getAddressCode(
val.regeocode.addressComponent.citycode,
val.regeocode.addressComponent.township
).then((code) => {
that.$emit("callback", { ...val, ...res, ...code });
that.$emit("close");
});
});
},
fail(e) {
console.log(e)
that.$emit("close");
},
});
},
// 根据当前客户端判断展示不同类型数据
init() {
// #ifdef MP-WEIXIN
this.wechatMap();
// #endif
// #ifndef MP-WEIXIN
this.initMap();
// #endif
},
const emit = defineEmits<{
callback: [payload: Record<string, any>]
close: []
}>()
// 如果是微信小程序单独走微信小程序授权模式
wechatMap() {
let that = this;
uni.authorize({
scope: "scope.userLocation",
success() {
// 允许授权
that.initMap();
},
fail() {
//拒绝授权
uni.showModal({
content: "检测到您没打开获取地址功能权限,是否去设置打开?",
confirmText: "确认",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
// 打开设置好后重新刷新地图
uni.openSetting({
success: (res) => {
that.initMap();
},
});
} else {
// 取消后关闭
that.$emit("close");
return false;
}
},
});
return false;
},
});
},
// 获取城市的数据
posToCity(latitude, longitude) {
return new Promise((resolve, reject) => {
uni.request({
url: `https://restapi.amap.com/v3/geocode/regeo`,
method: "GET",
data: {
key: config.aMapKey, //web服务的key
location: `${longitude},${latitude}`,
},
success: ({ data }) => {
const { status, info } = data;
if (status === "1") {
resolve(data);
} else {
reject(info);
}
},
fail: (err) => {
reject(err);
},
});
});
},
},
};
onMounted(() => {
init()
})
function initMap() {
uni.chooseLocation({
success(res) {
posToCity(res.latitude, res.longitude).then((val) => {
getAddressCode(
val.regeocode.addressComponent.citycode,
val.regeocode.addressComponent.township,
).then((code) => {
emit('callback', { ...val, ...res, ...code })
emit('close')
})
})
},
fail(e) {
console.log(e)
emit('close')
},
})
}
function init() {
// #ifdef MP-WEIXIN
wechatMap()
// #endif
// #ifndef MP-WEIXIN
initMap()
// #endif
}
function wechatMap() {
uni.authorize({
scope: 'scope.userLocation',
success() {
initMap()
},
fail() {
uni.showModal({
content: '检测到您没打开获取地址功能权限,是否去设置打开?',
confirmText: '确认',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: () => {
initMap()
},
})
} else {
emit('close')
}
},
})
},
})
}
function posToCity(latitude: number, longitude: number) {
return new Promise<any>((resolve, reject) => {
uni.request({
url: 'https://restapi.amap.com/v3/geocode/regeo',
method: 'GET',
data: {
key: config.aMapKey,
location: `${longitude},${latitude}`,
},
success: ({ data }: any) => {
const { status, info } = data
if (status === '1') {
resolve(data)
} else {
reject(info)
}
},
fail: (err) => {
reject(err)
},
})
})
}
</script>
<style lang="scss" scoped>
</style>
</style>

View File

@@ -1,9 +1,9 @@
<template>
<view>
<view
v-if="!hid"
v-if="!state.hid"
class="flex-row-center"
:style="{ top: scHight }"
:style="{ top: state.scHight }"
style="width: 750rpx; position: fixed; z-index: 100; left: 0"
>
<view
@@ -14,35 +14,35 @@
class="flex"
style="width: 100%"
animation="false"
:style="{ height: originalHeight }"
:style="{ height: state.originalHeight }"
>
<movable-view
scale-value="1"
animation="false"
damping="5000"
:x="moveX"
:x="state.moveX"
:style="{
height: sliderHeight,
width: sliderWidth,
height: state.sliderHeight,
width: state.sliderWidth,
'z-index': 101,
}"
direction="horizontal"
>
<image
:src="imgbk"
:src="state.imgbk"
class="image"
mode="aspectFit"
:style="{
height: sliderHeight,
width: sliderWidth,
'margin-top': imgbKH,
height: state.sliderHeight,
width: state.sliderWidth,
'margin-top': state.imgbKH,
}"
></image>
</movable-view>
<image
:src="img"
:src="state.img"
mode="aspectFit"
:style="{ height: originalHeight, width: originalWidth }"
:style="{ height: state.originalHeight, width: state.originalWidth }"
style="border-radius: 10rpx"
></image>
</movable-area>
@@ -61,7 +61,7 @@
scale-value="1"
animation="false"
damping="50"
:x="movePv"
:x="state.movePv"
class="flex-row-center"
style="
border-radius: 50%;
@@ -78,14 +78,14 @@
<u-icon
:color="mainColor"
size="40"
v-if="endLoad"
v-if="state.endLoad"
name="arrow-right"
></u-icon>
<u-icon :color="mainColor" size="40" v-else name="reload"></u-icon>
</movable-view>
<text style="padding-left: 140rpx" :style="{ color: col }">{{
hasImg
<text style="padding-left: 140rpx" :style="{ color: state.col }">{{
state.hasImg
}}</text>
</movable-area>
<view class="flex-row-around padding-top" style="width: 100%">
@@ -106,173 +106,178 @@
</view>
</template>
<script>
import api from "@/config/api.js";
import storage from "@/utils/storage.js";
import uuid from "@/utils/uuid.modified.js";
const phone = uni.getSystemInfoSync();
const l = phone.screenWidth / 750;
export default {
name: "verification",
created() {
// 可自行调整
this.scHight = phone.screenHeight / 2 - 200 + "px";
this.getCode();
},
props: {
height: {
type: String,
default: "80rpx",
<script setup lang="ts">
import { reactive, computed } from 'vue'
import { useStore } from '@/store'
import api from '@/config/api.js'
import storage from '@/utils/storage.js'
import uuid from '@/utils/uuid.modified.js'
const emit = defineEmits(['send'])
const props = withDefaults(defineProps<{
height?: string
width?: string
left?: string
top?: string
business?: string
}>(), {
height: '80rpx',
width: '350rpx',
left: '180rpx',
top: '30rpx',
business: 'LOGIN'
})
const store = useStore()
const phone = uni.getSystemInfoSync()
const l = phone.screenWidth / 750
const mainColor = computed(() => store.getters.mainColor)
const state = reactive({
flage: false,
key: '', // key
vsrtx: '点击进行验证', // 按钮提示语
vsr: false,
hid: true,
col: '#838383',
movePv: 0,
hasImg: '拖动滑块已完成拼图',
spcode: '',
tl: 0,
moveCode: 0,
// X轴移动距离
moveX: 0,
// 模版高度
originalHeight: '',
// 模版宽度
originalWidth: '',
// 拼图高度
sliderHeight: '',
// 平涂宽度
sliderWidth: '',
scHight: 0,
// 原图
img: '',
// 拼图
imgbk: '',
endLoad: true,
imgbKH: ''
})
// 可自行调整
state.scHight = phone.screenHeight / 2 - 200 + 'px'
getCode()
function show() {
state.hid = false
}
function hide() {
if (!state.vsr) {
// vsr判断是否验证成功成功隐藏验证框
state.hid = !state.hid
}
}
function error() {
state.vsr = false
state.hid = false
state.moveX = 0
state.moveCode = 0
}
// 获取验证图片
function getCode() {
state.col = '#b3afae'
state.hasImg = '图片加载中...'
if (!storage.getUuid()) {
storage.setUuid(uuid.v1())
}
uni.request({
url: api.common + '/common/slider/' + props.business,
header: {
uuid: storage.getUuid()
},
width: {
type: String,
default: "350rpx",
success: (res: any) => {
state.col = '#838383'
state.hasImg = '拖动滑块以完成拼图'
const data = res.data.result
// base64的图片
state.img = data.backImage
state.imgbk = data.slidingImage
// 根据参数动态适应验证图片的高宽
state.imgbKH = data.randomY * 1.8 + 'rpx'
state.originalHeight = data.originalHeight * 1.8 + 'rpx'
state.originalWidth = data.originalWidth * 1.8 + 'rpx'
state.sliderHeight = data.sliderHeight * 1.8 + 'rpx'
state.sliderWidth = data.sliderWidth * 1.8 + 'rpx'
// 适应比率,用来适应滑动距离
state.tl = 1 / (1.8 * l)
// 无用信息
state.spcode = data.capcode
// 验证令牌
state.key = data.key
store.state.verificationKey = data.key
}
})
}
function end() {
state.endLoad = false
// 验证拼图位置是否正确
uni.request({
method: 'POST',
url:
api.common +
'/common/slider/' +
props.business +
'?xPos=' +
parseInt(String(state.moveCode * state.tl)),
header: {
uuid: storage.getUuid()
},
left: {
type: String,
default: "180rpx",
},
top: {
type: String,
default: "30rpx",
},
business: {
type: String,
default: "LOGIN",
},
},
data() {
return {
mainColor: this.$mainColor,
flage: false,
key: "", //key
vsrtx: "点击进行验证", //按钮提示语
vsr: false, //
hid: true,
col: "#838383",
movePv: 0,
hasImg: "拖动滑块已完成拼图",
spcode: "",
tl: 0,
moveCode: 0,
//X轴移动距离
moveX: 0,
//模版高度
originalHeight: "",
//模版宽度
originalWidth: "",
//拼图高度
sliderHeight: "",
//平涂宽度
sliderWidth: "",
scHight: 0,
//原图
img: "",
//拼图
imgbk: "",
endLoad: true,
imgbKH: "",
};
},
methods: {
show() {
this.hid = false;
},
hide() {
if (!this.vsr) {
// vsr判断是否验证成功成功隐藏验证框
this.hid = !this.hid;
success: (res: any) => {
state.endLoad = true
if (res.data.result == false) {
res.data.result = false
} else {
res.data.result = true
}
if (res.data && res.data.result) {
// 验证成功后把key发送出去,后端会把验证信息存在缓存里
emit('send', state.key)
hide()
state.vsr = true
state.vsrtx = '已通过验证'
} else {
getCode() // 让滑块回到起始位置
if (state.movePv == 1) {
state.movePv = 0
} else {
state.movePv = 1
}
}
},
error() {
this.vsr = false;
this.hid = false;
this.moveX = 0;
this.moveCode = 0;
},
// 获取验证图片
getCode() {
this.col = "#b3afae";
this.hasImg = "图片加载中...";
if (!storage.getUuid()) {
storage.setUuid(uuid.v1());
}
uni.request({
url: api.buyer + "/common/slider/" + this.business,
header: {
uuid: storage.getUuid(),
},
success: (res) => {
this.col = "#838383";
this.hasImg = "拖动滑块以完成拼图";
var data = res.data.result;
fail: () => {
uni.showToast({ title: '连接服务器失败', icon: 'none' })
}
})
}
// base64的图片
this.img = data.backImage;
this.imgbk = data.slidingImage;
// 根据参数动态适应验证图片的高宽
this.imgbKH = data.randomY * 1.8 + "rpx";
this.originalHeight = data.originalHeight * 1.8 + "rpx";
this.originalWidth = data.originalWidth * 1.8 + "rpx";
this.sliderHeight = data.sliderHeight * 1.8 + "rpx";
this.sliderWidth = data.sliderWidth * 1.8 + "rpx";
// 适应比率,用来适应滑动距离
this.tl = 1 / (1.8 * l);
// 无用信息
this.spcode = data.capcode;
// 验证令牌
this.key = data.key;
this.$store.state.verificationKey = data.key;
},
});
},
end(e) {
this.endLoad = false;
// 验证拼图位置是否正确
uni.request({
method: "POST",
url:
api.buyer +
"/common/slider/" +
this.business +
"?xPos=" +
parseInt(this.moveCode * this.tl),
header: {
uuid: storage.getUuid(),
},
success: (res) => {
this.endLoad = true;
res.data.result == false
? (res.data.result = false)
: (res.data.result = true);
// 绑定拼图位置
function moveChange(e: any) {
state.moveX = e.detail.x
state.moveCode = e.detail.x
}
if (res.data && res.data.result) {
//验证成功后把key发送出去,后端会把验证信息存在缓存里
this.$emit("send", this.key);
this.hide();
this.vsr = true;
this.vsrtx = "已通过验证";
} else {
this.getCode(); // 让滑块回到起始位置
if (this.movePv == 1) {
this.movePv = 0;
} else {
this.movePv = 1;
}
}
},
fail: (res) => {
this.$msg("连接服务器失败");
},
});
},
// 绑定拼图位置
moveChange(e) {
this.moveX = e.detail.x;
this.moveCode = e.detail.x;
},
},
};
defineExpose({
show,
hide,
error,
getCode,
})
</script>
<style lang="scss" scoped>

View File

@@ -10,7 +10,7 @@
:password="isPassword"
:type="inputType"
:maxlength="size"
@input="input"
@input="handleInput"
@focus="inputFocus"
@blur="inputBlur"
/>
@@ -37,7 +37,7 @@
</view>
</view>
</template>
<script>
<script setup lang="ts">
/**
* @description 输入验证码组件
* @property {string} type = [box|middle|bottom] - 显示类型 默认box -eg:bottom
@@ -50,175 +50,155 @@
* @property {string} boxActiveColor - 光标聚焦到的框的颜色 默认:#000000
* @event {Function(data)} confirm - 输入完成
*/
export default {
name: 'xt-verify-code',
emits: ['update:modelValue', 'input', 'confirm'],
props: {
modelValue: {
type: String,
default: () => ''
},
value: {
type: String,
default: () => ''
},
type: {
type: String,
default: () => 'box'
},
inputType: {
type: String,
default: () => 'number'
},
size: {
type: Number,
default: () => 6
},
isFocus: {
type: Boolean,
default: () => true
},
isPassword: {
type: Boolean,
default: () => false
},
cursorColor: {
type: String,
default: () => '#cccccc'
},
boxNormalColor: {
type: String,
default: () => '#cccccc'
},
boxActiveColor: {
type: String,
default: () => '#000000'
}
},
data() {
return {
focused: false,
cursorVisible: false,
cursorHeight: 35,
code: '', // 输入的验证码
codeCursorLeft: [] // 向左移动的距离数组
};
},
created() {
this.code = this.modelValue || this.value || '';
this.focused = this.isFocus;
this.cursorVisible = this.isFocus;
},
mounted() {
this.init();
if (this.isFocus) {
this.$nextTick(() => {
this.focusInput();
});
}
},
methods: {
focusInput() {
this.focused = false;
this.$nextTick(() => {
this.focused = true;
// #ifdef H5
const inputEl = this.$refs.codeInput;
if (inputEl && typeof inputEl.focus === 'function') {
inputEl.focus();
}
// #endif
});
},
/**
* @description 初始化
*/
init() {
this.getCodeCursorLeft();
this.setCursorHeight();
},
/**
* @description 获取元素节点
* @param {string} elm - 节点的id、class 相当于 document.querySelect的参数 -eg: #id
* @param {string} type = [single|array] - 单个元素获取多个元素 默认是单个元素
* @param {Function} callback - 回调函数
*/
getElement(elm, type = 'single', callback) {
uni
.createSelectorQuery()
.in(this)
[type === 'array' ? 'selectAll' : 'select'](elm)
.boundingClientRect()
.exec(data => {
callback(data[0]);
});
},
/**
* @description 计算光标的高度
*/
setCursorHeight() {
this.getElement('.xt__box', 'single', boxElm => {
this.cursorHeight = boxElm.height * 0.6;
});
},
/**
* @description 获取光标在每一个box的left位置
*/
getCodeCursorLeft() {
// 获取父级框的位置信息
this.getElement('#xt__input-ground', 'single', parentElm => {
const parentLeft = parentElm.left;
// 获取各个box信息
this.getElement('.xt__box', 'array', elms => {
this.codeCursorLeft = [];
elms.forEach(elm => {
this.codeCursorLeft.push(elm.left - parentLeft + elm.width / 2);
});
});
});
},
import { ref, watch, onMounted, nextTick, getCurrentInstance } from 'vue'
// 输入框输入变化的回调
input(e) {
const value = e.detail.value;
this.code = value;
this.cursorVisible = value.length !== this.size;
this.$emit('update:modelValue', value);
this.$emit('input', value);
this.inputSuccess(value);
},
const emit = defineEmits(['update:modelValue', 'input', 'confirm'])
const props = withDefaults(defineProps<{
modelValue?: string
value?: string
type?: string
inputType?: string
size?: number
isFocus?: boolean
isPassword?: boolean
cursorColor?: string
boxNormalColor?: string
boxActiveColor?: string
}>(), {
modelValue: '',
value: '',
type: 'box',
inputType: 'number',
size: 6,
isFocus: true,
isPassword: false,
cursorColor: '#cccccc',
boxNormalColor: '#cccccc',
boxActiveColor: '#000000'
})
// 输入完成回调
inputSuccess(value) {
if (value.length === this.size) {
this.$emit('confirm', value);
}
},
// 输入聚焦
inputFocus() {
this.cursorVisible = this.code.length !== this.size;
},
// 输入失去焦点
inputBlur() {
this.cursorVisible = false;
},
codeFormat(val, isPassword) {
let value = '';
if (val) {
value = isPassword ? '*' : val;
}
return value;
}
},
watch: {
modelValue(val) {
this.code = val || '';
},
value(val) {
this.code = val || '';
}
const instance = getCurrentInstance()
const codeInput = ref<any>(null)
const focused = ref(false)
const cursorVisible = ref(false)
const cursorHeight = ref(35)
const code = ref('') // 输入的验证码
const codeCursorLeft = ref<number[]>([]) // 向左移动的距离数组
// 初始化
watch([() => props.modelValue, () => props.value], ([modelVal, val]) => {
code.value = modelVal || val || ''
}, { immediate: true })
onMounted(() => {
focused.value = props.isFocus
cursorVisible.value = props.isFocus
init()
if (props.isFocus) {
nextTick(() => {
focusInput()
})
}
};
})
function focusInput() {
focused.value = false
nextTick(() => {
focused.value = true
// #ifdef H5
const inputEl = codeInput.value
if (inputEl && typeof inputEl.focus === 'function') {
inputEl.focus()
}
// #endif
})
}
/**
* @description 初始化
*/
function init() {
getCodeCursorLeft()
setCursorHeight()
}
/**
* @description 获取元素节点
* @param {string} elm - 节点的id、class 相当于 document.querySelect的参数 -eg: #id
* @param {string} type = [single|array] - 单个元素获取多个元素 默认是单个元素
* @param {Function} callback - 回调函数
*/
function getElement(elm: string, type: string = 'single', callback: Function) {
uni
.createSelectorQuery()
.in(instance?.proxy)
[type === 'array' ? 'selectAll' : 'select'](elm)
.boundingClientRect()
.exec((data: any) => {
callback(data[0])
})
}
/**
* @description 计算光标的高度
*/
function setCursorHeight() {
getElement('.xt__box', 'single', (boxElm: any) => {
cursorHeight.value = boxElm.height * 0.6
})
}
/**
* @description 获取光标在每一个box的left位置
*/
function getCodeCursorLeft() {
// 获取父级框的位置信息
getElement('#xt__input-ground', 'single', (parentElm: any) => {
const parentLeft = parentElm.left
// 获取各个box信息
getElement('.xt__box', 'array', (elms: any) => {
codeCursorLeft.value = []
elms.forEach((elm: any) => {
codeCursorLeft.value.push(elm.left - parentLeft + elm.width / 2)
})
})
})
}
// 输入框输入变化的回调
function handleInput(e: any) {
const value = e.detail.value
code.value = value
cursorVisible.value = value.length !== props.size
emit('update:modelValue', value)
emit('input', value)
inputSuccess(value)
}
// 输入完成回调
function inputSuccess(value: string) {
if (value.length === props.size) {
emit('confirm', value)
}
}
// 输入聚焦
function inputFocus() {
cursorVisible.value = code.value.length !== props.size
}
// 输入失去焦点
function inputBlur() {
cursorVisible.value = false
}
function codeFormat(val: string | undefined, isPassword: boolean): string {
let value = ''
if (val) {
value = isPassword ? '*' : val
}
return value
}
</script>
<style lang="scss" scoped>
.xt__verify-code {

View File

@@ -74,7 +74,7 @@ function sendMessage(price) {
uni.requestSubscribeMessage({
tmplIds: templateid,
success: (res) => {
},
fail: (res) => {
console.log('fail', res)

View File

@@ -38,6 +38,12 @@ export function createApp() {
config: {
loadFontOnce: true,
},
props: {
form: {
labelWidth: '180rpx',
labelPosition: 'left',
},
},
})
app.config.globalProperties.$store = store

View File

@@ -126,7 +126,7 @@
"path": "myCollect",
"style": {
"navigationBarTitleText": "收藏",
"enablePullDownRefresh": true, //下拉刷新
"enablePullDownRefresh": false,
"navigationStyle": "custom",
"app-plus": {
"scrollIndicator": "none"
@@ -580,6 +580,7 @@
}
},
// #ifndef MP-WEIXIN
{
"path": "live/room",
"style": {
@@ -588,6 +589,7 @@
"disableScroll": true
}
},
// #endif
{
"path": "joinGroup",
"style": {
@@ -599,14 +601,17 @@
"bounce": "none"
}
}
},{
},
// #ifndef MP-WEIXIN
{
"path": "lives",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "black"
}
},{
},
// #endif
{
"path": "bargain/list",
"style": {
"navigationStyle": "custom",
@@ -699,6 +704,17 @@
}
}
},
{
"path": "payment/error",
"style": {
"navigationBarTitleText": "支付失败",
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"app-plus": {
"popGesture": "none"
}
}
},
{
"path": "payment/shareOrderGoods",
"style": {

View File

@@ -1,302 +1,262 @@
<template>
<view class="coupon-center">
<div class="swiper-box">
<div class="swiper-item">
<div class="scroll-v" enableBackToTop="true" scroll-y>
<u-empty mode="coupon" style='margin-top: 20%;' text="没有优惠券了" v-if="whetherEmpty"></u-empty>
<view v-else class="coupon-item" v-for="(item, index) in couponList" :key="index">
<view class="left">
<view class="wave-line">
<view class="wave" v-for="(item, index) in 12" :key="index"></view>
</view>
<view class="message">
<view>
<!--判断当前优惠券类型 couponType PRICE || DISCOUNT -->
<span v-if="item.couponType == 'DISCOUNT'">{{ item.couponDiscount }}</span>
<span v-else>{{ item.price }}</span>
</view>
<view>{{unitPrice(item.consumeThreshold) }}元可用</view>
</view>
<view class="circle circle-top"></view>
<view class="circle circle-bottom"></view>
</view>
<view class="right">
<view>
<!-- 根据scopeType 判断是否是 平台品类或店铺 -->
<view class="coupon-title wes-3" v-if="item.scopeType">
<span v-if="item.scopeType == 'ALL' && item.storeId == '0'">全平台</span>
<span v-if="item.scopeType == 'PORTION_GOODS_CATEGORY'">仅限品类</span>
<view v-else>{{ item.storeName == 'platform' ? '全平台' :item.storeName+'店铺' }}使用
</view>
</view>
<view v-if="item.endTime">有效期至:{{ item.endTime.split(" ")[0] }}</view>
</view>
<view class="receive" @click="receive(item)">
<text>点击</text><br />
<text>领取</text>
</view>
<view class="bg-quan"> </view>
</view>
<view class="coupon-list">
<u-empty mode="coupon" style='margin-top: 20%;' text="没有优惠券了" v-if="whetherEmpty"></u-empty>
<view v-else class="coupon-card" v-for="(item, index) in couponList" :key="index">
<view class="coupon-card-left">
<text class="coupon-price-symbol">¥</text>
<text class="coupon-price-value">{{ item.couponType == 'DISCOUNT' ? item.couponDiscount + '折' : unitPrice(item.price) }}</text>
</view>
<view class="coupon-divider"></view>
<view class="coupon-card-right">
<view class="coupon-info">
<text class="coupon-card-name">{{ item.storeName == 'platform' ? '全平台' : item.storeName + '店铺' }}使用</text>
<text class="coupon-card-desc">{{unitPrice(item.consumeThreshold) }}元可用</text>
<text class="coupon-card-time" v-if="item.endTime">有效期至:{{ item.endTime.split(" ")[0] }}</text>
</view>
</div>
</div>
</div>
<view class="coupon-claim-btn" @click="receive(item)">
领取
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
receiveCoupons
} from "@/api/members.js";
import {
getAllCoupons
} from "@/api/promotions.js";
export default {
data() {
return {
loadStatus: "more", //下拉状态
whetherEmpty: false, //是否为空
couponList: [], // 优惠券列表
params: {
pageNumber: 1,
pageSize: 10,
},
storeId: "", //店铺 id,
couponData: ""
};
},
onLoad(option) {
this.storeId = option.storeId;
this.getCoupon();
},
onReachBottom() {
<script setup lang="ts">
import { receiveCoupons } from '@/api/members.js'
import { getAllCoupons } from '@/api/promotions.js'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import {
onLoad,
onNavigationBarButtonTap,
onPullDownRefresh,
onReachBottom,
} from '@dcloudio/uni-app'
import { getCurrentInstance, ref } from 'vue'
this.loadMore()
},
onPullDownRefresh() {
//下拉刷新
this.params.pageNumber = 1;
this.couponList = [];
this.getCoupon();
},
methods: {
/**
* 获取当前优惠券
*/
getCoupon() {
uni.showLoading({
title: "加载中",
});
let submitData = {
...this.params
};
// 判断当前是否有店铺
this.storeId ? (submitData = {
...this.params,
storeId: this.storeId
}) : "",
getAllCoupons(submitData)
.then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
uni.stopPullDownRefresh();
if (res.data.code == 200) {
// 如果请求成功,展示数据并进行展示
this.couponData = res.data.result
if (this.couponData.total == 0) {
// 当本次请求数据为空展示空信息
this.whetherEmpty = true;
} else {
this.couponList.push(...this.couponData.records);
this.loadStatus = "noMore";
}
}
})
.catch((err) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
/**
* 领取优惠券
*/
receive(val) {
this.$u.throttle(()=>{
this.fetchCoupon(val)
}, 1500)
},
const store = useStore()
const { proxy } = getCurrentInstance()!
fetchCoupon(val){
receiveCoupons(val.id).then((res) => {
if (res.data.code == 200) {
uni.showToast({
title: "领取成功",
icon: "none",
});
} else {
uni.showToast({
title: res.data.message,
icon: "none",
});
}
});
},
const loadStatus = ref('more')
const whetherEmpty = ref(false)
const couponList = ref<any[]>([])
const params = ref({
pageNumber: 1,
pageSize: 10,
})
const storeId = ref('')
const couponData = ref<any>(null)
/**
* 加载更多
*/
loadMore() {
if (this.couponData.total > this.params.pageNumber * this.params.pageSize) {
this.params.pageNumber++;
this.getCoupon();
}
},
},
onNavigationBarButtonTap(e) {
uni.navigateTo({
url: "/pages/cart/coupon/couponIntro",
});
},
};
onLoad((option) => {
storeId.value = option.storeId || ''
getCoupon()
})
onReachBottom(() => {
loadMore()
})
onPullDownRefresh(() => {
params.value.pageNumber = 1
couponList.value = []
getCoupon()
})
onNavigationBarButtonTap(() => {
uni.navigateTo({
url: '/pages/cart/coupon/couponIntro',
})
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function getCoupon() {
uni.showLoading({ title: '加载中' })
const submitData = storeId.value
? { ...params.value, storeId: storeId.value }
: { ...params.value }
getAllCoupons(submitData)
.then((res) => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
if (res.data.code == 200) {
couponData.value = res.data.result
if (couponData.value.total == 0) {
whetherEmpty.value = true
} else {
couponList.value.push(...couponData.value.records)
loadStatus.value = 'noMore'
}
}
})
.catch(() => {
hideLoadingIfNeeded()
})
}
function receive(val: any) {
proxy.$u.throttle(() => {
fetchCoupon(val)
}, 1500)
}
function fetchCoupon(val: any) {
receiveCoupons(val.id).then((res) => {
if (res.data.code == 200) {
uni.showToast({ title: '领取成功', icon: 'none' })
} else {
uni.showToast({ title: res.data.message, icon: 'none' })
}
})
}
function loadMore() {
if (couponData.value?.total > params.value.pageNumber * params.value.pageSize) {
params.value.pageNumber++
getCoupon()
}
}
</script>
<style>
page {
height: 100%;
}
</style>
<style lang="scss" scoped>
.coupon-center {
height: 100%;
.coupon-center {
min-height: 100vh;
background: #f7f8fa;
}
.swiper-box {
.coupon-item {
display: flex;
align-items: center;
height: 220rpx;
margin: 20rpx;
.coupon-list {
padding: 24rpx;
}
.left {
height: 100%;
width: 260rpx;
background-color: $light-color;
position: relative;
.coupon-card {
display: flex;
align-items: stretch;
background: #fff;
border-radius: 24rpx;
margin-bottom: 24rpx;
position: relative;
box-shadow: none;
border: 1rpx solid #f0f1f5;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
.message {
color: $font-color-white;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 40rpx;
&:active {
transform: scale(0.98);
}
view:nth-child(1) {
font-weight: bold;
font-size: 60rpx;
}
&::before,
&::after {
content: "";
position: absolute;
width: 32rpx;
height: 32rpx;
background: #f7f8fa;
border-radius: 50%;
left: 204rpx;
z-index: 2;
box-shadow: inset 0 0 0 1rpx #eceef2;
}
view:nth-child(2) {
font-size: $font-sm;
}
}
&::before {
top: -16rpx;
}
.wave-line {
height: 220rpx;
width: 8rpx;
position: absolute;
top: 0;
left: 0;
background-color: $light-color;
overflow: hidden;
&::after {
bottom: -16rpx;
}
}
.wave {
width: 8rpx;
height: 16rpx;
background-color: #ffffff;
border-radius: 0 16rpx 16rpx 0;
margin-top: 4rpx;
}
}
.coupon-card-left {
width: 220rpx;
min-height: 180rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: #ff3b30;
position: relative;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
}
.circle {
width: 40rpx;
height: 40rpx;
background-color: $bg-color;
position: absolute;
border-radius: 50%;
z-index: 111;
}
.coupon-price-symbol {
font-size: 32rpx;
font-weight: 700;
margin-right: 4rpx;
}
.circle-top {
top: -20rpx;
right: -20rpx;
}
.coupon-price-value {
font-size: 56rpx;
font-weight: 900;
line-height: 1;
letter-spacing: -1rpx;
}
.circle-bottom {
bottom: -20rpx;
right: -20rpx;
}
}
.coupon-divider {
position: absolute;
left: 220rpx;
top: 24rpx;
bottom: 24rpx;
width: 0;
border-left: 2rpx dashed #eceef2;
}
.right {
display: flex;
justify-content: space-between;
align-items: center;
width: 450rpx;
font-size: $font-sm;
height: 100%;
background-color: #ffffff;
overflow: hidden;
position: relative;
.coupon-card-right {
flex: 1;
padding: 32rpx 32rpx 32rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
background: #fff;
}
>view:nth-child(1) {
color: #666666;
margin-left: 20rpx;
display: flex;
height: 100%;
flex-direction: column;
justify-content: space-around;
.coupon-info {
display: flex;
flex-direction: column;
gap: 12rpx;
flex: 1;
min-width: 0;
}
>view:nth-child(1) {
color: #ff6262;
font-size: 30rpx;
}
}
.coupon-card-name {
font-size: 30rpx;
color: #111;
font-weight: 700;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.receive {
color: #ffffff;
background-color: $main-color;
border-radius: 50%;
width: 86rpx;
height: 86rpx;
text-align: center;
margin-right: 30rpx;
vertical-align: middle;
padding-top: 8rpx;
position: relative;
z-index: 2;
}
.coupon-card-desc {
font-size: 24rpx;
color: #8a8f99;
font-weight: 500;
}
.bg-quan {
width: 244rpx;
height: 244rpx;
border: 6rpx solid $main-color;
border-radius: 50%;
opacity: 0.1;
color: $main-color;
text-align: center;
padding-top: 30rpx;
font-size: 130rpx;
position: absolute;
right: -54rpx;
bottom: -60rpx;
}
}
}
}
}
.coupon-card-time {
font-size: 22rpx;
color: #b0b3bf;
}
.coupon-title {
width: 260rpx;
.coupon-claim-btn {
flex-shrink: 0;
padding: 16rpx 32rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, #ff6b35, #ff4b2b);
box-shadow: 0 8rpx 16rpx rgba(255, 75, 43, 0.25);
}
&:active {
opacity: 0.9;
}
}
</style>

View File

@@ -2,52 +2,46 @@
<view class="content">
<view class="body">
<view class="top-view">
<view class="title">{{coupon.title}}</view>
<view class="price" v-if="coupon.couponType =='PRICE'"><text></text>{{unitPrice(coupon.price)}}</view>
<view class="price" v-if="coupon.couponType =='DISCOUNT'">{{coupon.discount}}</view>
<view class="text">{{coupon.consumeThreshold}}元可用</view>
<view class="bg-quan">
</view>
<view class="jiao-1" :class="{'used-color':coupon.used_status!=0}">
<text class="text-1">{{coupon.used_status == 0?'新到':coupon.used_status_text}}</text>
<view class="title">{{ coupon.title }}</view>
<view class="price" v-if="coupon.couponType == 'PRICE'"><text></text>{{ unitPrice(coupon.price) }}</view>
<view class="price" v-if="coupon.couponType == 'DISCOUNT'">{{ coupon.discount }}</view>
<view class="text">{{ coupon.consumeThreshold }}元可用</view>
<view class="bg-quan"></view>
<view class="jiao-1" :class="{ 'used-color': coupon.used_status != 0 }">
<text class="text-1">{{ coupon.used_status == 0 ? '新到' : coupon.used_status_text }}</text>
</view>
</view>
<view class="bottom-view">
<view class="text"> 使用范围{{
coupon.scopeType == 'ALL' && coupon.storeId == '0'
? "全平台"
: coupon.scopeType == "PORTION_GOODS"
? "部分商品"
: coupon.scopeType == "PORTION_GOODS_CATEGORY"
? "部分分类商品"
: coupon.storeName == 'platform' ? '全平台' :coupon.storeName+''
}}使用</view>
<view class="text"> 有效期至{{coupon.endTime}}</view>
coupon.scopeType == 'ALL' && coupon.storeId == '0'
? '全平台'
: coupon.scopeType == 'PORTION_GOODS'
? '部分商品'
: coupon.scopeType == 'PORTION_GOODS_CATEGORY'
? '部分分类商品'
: coupon.storeName == 'platform' ? '全平台' : coupon.storeName + ''
}}使用</view>
<view class="text"> 有效期至{{ coupon.endTime }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
coupon: {}, //优惠券数据
};
},
onLoad(option) {
this.coupon = JSON.parse(decodeURIComponent(option.item));
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { unitPrice } from '@/utils/filters.js'
const coupon = ref<Record<string, any>>({})
onLoad((option) => {
coupon.value = JSON.parse(decodeURIComponent(option.item))
})
</script>
<style lang="scss" scoped>
page,
.content {
// background: $main-color;
height: 100%;
}
.body {

View File

@@ -1,294 +1,323 @@
<template>
<div class="wrapper">
<u-tabs
class="coupon-tabs"
:list="list"
:scrollable="false"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
v-model:current="current"
>
</u-tabs>
<div class="empty" v-if="couponsList.length <= 0">
<u-empty text="暂无优惠券" mode="coupon"></u-empty>
</div>
<view class="coupon-item" v-for="(item, index) in couponsList" :key="index">
<view class="left">
<view class="wave-line">
<view class="wave" v-for="(item, index) in 12" :key="index"></view>
</view>
<view class="message">
<view>
<span v-if="item.couponType == 'DISCOUNT'">{{ item.discount }}</span>
<span v-else>{{ item.price }}</span>
</view>
<view>{{unitPrice(item.consumeThreshold) }}元可用</view>
</view>
<view class="circle circle-top"></view>
<view class="circle circle-bottom"></view>
</view>
<view class="right">
<view class="desc">
<view v-if="item.scopeType">
<span v-if="item.scopeType == 'ALL' && item.storeId == '0'">全平台</span>
<span v-if="item.scopeType == 'PORTION_GOODS_CATEGORY'">仅限品类</span>
<view v-else
>{{
item.storeName == "platform" ? "全平台" : item.storeName + "店铺"
}}使用</view
>
</view>
<view class="reason" v-if="item.reason">{{ item.reason }}</view>
<view class="end-time">有效期至:{{ item.endTime }}</view>
</view>
<view
class="receive"
v-if="current == 0 && !routerVal.selectedCoupon.includes(item.id)"
@click="clickWay(item)"
>
<text>立即</text><br />
<text>使用</text>
</view>
<view class="used" v-if="current == 0 && routerVal.selectedCoupon.includes(item.id)" @click="clickWay(item)">
<text>取消</text><br />
<text>使用</text>
</view>
<view class="bg-quan"></view>
</view>
<view class="b-content" :style="themeStyle">
<view class="coupon-tabs">
<u-tabs
:list="list"
:scrollable="false"
:inactiveStyle="{ color: '#333' }"
v-model:current="current"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
></u-tabs>
</view>
</div>
<scroll-view class="list-scroll-content" scroll-y>
<u-empty
mode="coupon"
text="暂无优惠券"
v-if="couponsList.length <= 0"
></u-empty>
<view
class="coupon-card"
:class="{ 'coupon-used': current != 0 }"
v-for="(item, index) in couponsList"
:key="item.id || index"
>
<view class="coupon-card-left">
<text class="coupon-price-symbol" v-if="item.couponType != 'DISCOUNT'">¥</text>
<text class="coupon-price-value">
{{ item.couponType == 'DISCOUNT' ? item.discount + '折' : unitPrice(item.price) }}
</text>
</view>
<view class="coupon-divider"></view>
<view class="coupon-card-right">
<view class="coupon-info">
<text class="coupon-card-name">
<template v-if="item.scopeType == 'ALL' && item.storeId == '0'">全平台</template>
<template v-else-if="item.scopeType == 'PORTION_GOODS_CATEGORY'">仅限品类</template>
<template v-else>
{{ item.storeName == 'platform' ? '全平台' : (item.storeName || '') + '店铺' }}
</template>
使用
</text>
<text class="coupon-card-desc">{{ unitPrice(item.consumeThreshold) }}元可用</text>
<text class="coupon-card-time" v-if="item.reason">{{ item.reason }}</text>
<text class="coupon-card-time" v-if="item.endTime">有效期至:{{ item.endTime }}</text>
</view>
<view
class="coupon-status-btn"
v-if="current == 0 && !isSelected(item)"
@click="clickWay(item)"
>
立即使用
</view>
<view
class="coupon-status-btn cancel"
v-else-if="current == 0 && isSelected(item)"
@click="clickWay(item)"
>
取消使用
</view>
<view class="coupon-status-btn disabled" v-else>
不可用
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import { useCoupon } from "@/api/trade.js";
export default {
data() {
return {
lightColor: this.$lightColor,
current: 0,
list: [
{
name: "可用优惠券",
},
{
name: "不可用优惠券",
},
],
couponsList: [], //优惠券集合
params: {
//传参
memberCouponStatus: "NEW", //优惠券状态
pageNumber: 1,
pageSize: 10,
scopeId: "", //商品skuid
storeId: "", //店铺id
totalPrice: "", //价格
},
routerVal: "", //上级传参
};
},
onLoad(options) {
this.routerVal = options;
},
watch: {
current(val) {
console.log(this.$store.state.cantUseCoupons);
val == 0
? (this.couponsList = this.$store.state.canUseCoupons)
: (this.couponsList = this.$store.state.cantUseCoupons);
},
},
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { useCoupon } from '@/api/trade.js'
import { unitPrice } from '@/utils/filters.js'
import { getThemeStyle } from '@/utils/theme'
mounted() {
this.init();
console.log(this.routerVal);
},
const store = useStore()
methods: {
/**
* 从vuex中拿取优惠券信息
*/
init() {
this.couponsList = this.$store.state.canUseCoupons;
},
/**
* 领取优惠券
*/
clickWay(coupon) {
useCoupon({
memberCouponId: coupon.id,
used: !this.routerVal.selectedCoupon.includes(coupon.id),
way: this.routerVal.way,
}).then((res) => {
if (res.data.success) {
uni.navigateBack();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
},
},
};
const lightColor = computed(() => store.getters.lightColor)
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const current = ref(0)
const list = [
{ name: '可用优惠券' },
{ name: '不可用优惠券' },
]
const couponsList = ref<any[]>([])
const routerVal = ref<Record<string, any>>({
selectedCoupon: [],
way: '',
})
onLoad((options) => {
const selectedCoupon = options?.selectedCoupon
let selected: string[] = []
if (Array.isArray(selectedCoupon)) {
selected = selectedCoupon
} else if (typeof selectedCoupon === 'string' && selectedCoupon) {
selected = selectedCoupon.split(',')
}
routerVal.value = {
...(options || {}),
selectedCoupon: selected,
}
})
watch(current, (val) => {
couponsList.value = val == 0
? store.state.canUseCoupons
: store.state.cantUseCoupons
})
onMounted(() => {
couponsList.value = store.state.canUseCoupons || []
})
function isSelected(coupon: any) {
return (routerVal.value.selectedCoupon || []).includes(coupon.id)
}
function clickWay(coupon: any) {
useCoupon({
memberCouponId: coupon.id,
used: !isSelected(coupon),
way: routerVal.value.way,
}).then((res) => {
if (res.data.success) {
uni.navigateBack()
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
</script>
<style scoped lang="scss">
.desc {
height: 220rpx;
flex: 2;
<style lang="scss" scoped>
.b-content {
background: #f7f8fa;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.end-time,
.reason {
color: #999;
line-height: 1.5;
font-size: 24rpx;
}
.empty {
margin-top: 20px;
text-align: center;
.coupon-tabs {
background: #fff;
height: 88rpx;
box-shadow: 0 1rpx 8rpx rgba(0, 0, 0, 0.04);
position: relative;
z-index: 2;
flex-shrink: 0;
}
.wrapper {
background: #f9f9f9;
:deep(.u-tabs),
:deep(.u-tabs__wrapper),
:deep(.u-tabs__wrapper__scroll-view-wrapper),
:deep(.u-tabs__wrapper__scroll-view),
:deep(.u-tabs__wrapper__nav) {
background: #fff;
height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item) {
min-height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item__text) {
color: #333;
font-size: 28rpx;
}
.list-scroll-content {
flex: 1;
height: 0;
width: 100%;
padding: 24rpx;
box-sizing: border-box;
}
.coupon-card {
display: flex;
align-items: stretch;
background: #fff;
border-radius: 24rpx;
margin-bottom: 24rpx;
position: relative;
border: 1rpx solid #f0f1f5;
overflow: hidden;
.coupon-tabs {
width: 100%;
background: #fff;
&:active {
transform: scale(0.98);
}
&::before,
&::after {
content: '';
position: absolute;
width: 32rpx;
height: 32rpx;
background: #f7f8fa;
border-radius: 50%;
left: 204rpx;
z-index: 2;
box-shadow: inset 0 0 0 1rpx #eceef2;
}
&::before {
top: -16rpx;
}
&::after {
bottom: -16rpx;
}
&.coupon-used {
.coupon-card-left {
background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
color: #999;
}
}
}
.coupon-item {
.coupon-card-left {
width: 220rpx;
min-height: 180rpx;
display: flex;
align-items: center;
height: 220rpx;
margin: 20rpx;
justify-content: center;
flex-shrink: 0;
color: #ff3b30;
position: relative;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
}
.left {
height: 100%;
width: 260rpx;
background-color: $light-color;
position: relative;
.message {
color: $font-color-white;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 40rpx;
.coupon-price-symbol {
font-size: 32rpx;
font-weight: 700;
margin-right: 4rpx;
}
view:nth-child(1) {
font-weight: bold;
font-size: 60rpx;
}
.coupon-price-value {
font-size: 56rpx;
font-weight: 900;
line-height: 1;
letter-spacing: -1rpx;
}
view:nth-child(2) {
font-size: $font-sm;
}
}
.coupon-divider {
position: absolute;
left: 220rpx;
top: 24rpx;
bottom: 24rpx;
width: 0;
border-left: 2rpx dashed #eceef2;
}
.wave-line {
height: 220rpx;
width: 8rpx;
position: absolute;
top: 0;
left: 0;
background-color: $light-color;
overflow: hidden;
.coupon-card-right {
flex: 1;
padding: 32rpx 32rpx 32rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
background: #fff;
}
.wave {
width: 8rpx;
height: 16rpx;
background-color: #ffffff;
border-radius: 0 16rpx 16rpx 0;
margin-top: 4rpx;
}
}
.circle {
width: 40rpx;
height: 40rpx;
background-color: $bg-color;
position: absolute;
border-radius: 50%;
z-index: 111;
}
.circle-top {
top: -20rpx;
right: -20rpx;
}
.circle-bottom {
bottom: -20rpx;
right: -20rpx;
}
.coupon-info {
display: flex;
flex-direction: column;
gap: 12rpx;
flex: 1;
min-width: 0;
}
.coupon-card-name {
font-size: 30rpx;
color: #111;
font-weight: 700;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.coupon-card-desc {
font-size: 24rpx;
color: #8a8f99;
font-weight: 500;
}
.coupon-card-time {
font-size: 22rpx;
color: #b0b3bf;
}
.coupon-status-btn {
flex-shrink: 0;
padding: 16rpx 28rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, var(--theme-light, #ff6b35), var(--theme-primary, #ff4b2b));
&:active {
opacity: 0.9;
}
.right {
display: flex;
justify-content: space-between;
align-items: center;
width: 450rpx;
font-size: $font-sm;
height: 220rpx;
background-color: #ffffff;
overflow: hidden;
position: relative;
> view:nth-child(1) {
color: #666666;
margin-left: 20rpx;
&.cancel {
background: #333;
}
> view:nth-child(1) {
color: #ff6262;
font-size: 30rpx;
}
}
.receive {
color: #ffffff;
background-color: $main-color;
border-radius: 50%;
width: 86rpx;
height: 86rpx;
text-align: center;
margin-right: 30rpx;
vertical-align: middle;
padding-top: 8rpx;
position: relative;
z-index: 2;
}
.used {
color: #ffffff;
background-color: black;
border-radius: 50%;
width: 86rpx;
height: 86rpx;
text-align: center;
margin-right: 30rpx;
vertical-align: middle;
padding-top: 8rpx;
position: relative;
z-index: 2;
}
.bg-quan {
width: 244rpx;
height: 244rpx;
border: 6rpx solid $main-color;
border-radius: 50%;
opacity: 0.1;
color: $main-color;
text-align: center;
padding-top: 30rpx;
font-size: 130rpx;
position: absolute;
right: -54rpx;
bottom: -60rpx;
}
&.disabled {
background: #ccc;
}
}
</style>

View File

@@ -1,471 +1,393 @@
<template>
<view class="b-content">
<view class="navbar">
<!-- 循环出头部tab栏 -->
<view
v-for="(item, index) in navList"
:key="index"
class="nav-item"
@click="handleTabClick(index)"
><text :class="{ current: tabCurrentIndex === index }">{{
item.text
}}</text></view
>
</view>
<swiper
:current="tabCurrentIndex"
class="swiper-box"
duration="300"
@change="changeTab"
>
<swiper-item
class="tab-content"
v-for="(navItem, navIndex) in navList"
:key="navIndex"
>
<scroll-view
class="list-scroll-content"
scroll-y
@scrolltolower="loadData"
>
<!-- 空白页 -->
<u-empty
mode="coupon"
text="暂无优惠券了"
v-if="navItem.whetherEmpty"
></u-empty>
<!-- 数据 -->
<view
class="coupon-item"
:class="{ 'coupon-used': navIndex != 0 }"
v-for="(coupon, index) in navItem.dataList"
:key="index"
>
<view class="left">
<view class="wave-line">
<view
class="wave"
v-for="(item, index) in 12"
:key="index"
></view>
</view>
<view class="message">
<view class="price" v-if="coupon.couponType == 'DISCOUNT'"
>{{ coupon.discount }}</view
>
<view class="price" v-else>{{ coupon.price }}</view>
<view class="sub-price"
>{{unitPrice(coupon.consumeThreshold) }}可用</view
>
</view>
<view class="circle circle-top"></view>
<view class="circle circle-bottom"></view>
</view>
<view class="right" v-if="coupon">
<view class="content">
<view class="title-1">{{ coupon.title }}</view>
<view class="title-2"
>使用范围{{
coupon.scopeType == "ALL" && coupon.storeId == "0"
? "全平台"
: coupon.scopeType == "PORTION_GOODS"
? "部分商品"
: coupon.scopeType == "PORTION_GOODS_CATEGORY"
? "部分分类商品"
: coupon.storeName == "platform"
? "全平台"
: coupon.storeName + ""
}}使用</view
>
<view v-if="coupon.endTime">{{ coupon.endTime }}</view>
<view @click="couponDetail(coupon)"
>详细说明
<u-icon
style="float: right; margin-top: 10rpx"
name="arrow-right"
></u-icon>
</view>
</view>
<view class="jiao-1" v-if="navIndex == 0">
<text class="text-1">新到</text>
<text class="text-2" v-if="coupon.used_status == 1"
>将过期</text
>
</view>
<image
class="no-icon"
v-if="navIndex == 1"
src="@/static/img/used.png"
></image>
<image
class="no-icon"
v-if="navIndex == 2"
src="@/static/img/overdue.png"
></image>
<view
class="receive"
v-if="navIndex == 0"
@click="useItNow(coupon)"
>
<text>立即</text><br />
<text>使用</text>
</view>
<view class="bg-quan"> </view>
</view>
</view>
<uni-load-more :status="navItem.loadStatus"></uni-load-more>
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import { getMemberCoupons } from "@/api/members.js";
export default {
data() {
return {
tabCurrentIndex: 0, //tab栏下标默认为0 未使用
navList: [
//每个tab存储的信息
{
text: "未使用",
loadStatus: "more",
dataList: [],
params: {
memberCouponStatus: "NEW",
pageNumber: 1,
pageSize: 10,
status: 1,
},
whetherEmpty: false,
},
{
text: "已使用",
loadStatus: "more",
dataList: [],
params: {
memberCouponStatus: "USED",
pageNumber: 1,
pageSize: 10,
status: 2,
},
whetherEmpty: false,
},
{
text: "已过期",
loadStatus: "more",
dataList: [],
params: {
memberCouponStatus: "EXPIRE",
pageNumber: 1,
pageSize: 10,
status: 3,
},
whetherEmpty: false,
},
],
couponList: [], //优惠券列表
};
},
onShow() {
this.navList[this.tabCurrentIndex].params.pageNumber = 1;
this.navList[this.tabCurrentIndex].dataList = [];
this.getData();
},
watch: {
/**
* 监听切换顶部tab栏实现刷新数据
*/
tabCurrentIndex(val) {
if (this.navList[val].dataList.length == 0) this.getData();
},
},
methods: {
/**
* 顶部tab点击
*/
handleTabClick(index) {
this.tabCurrentIndex = index;
},
/**
* 读取优惠券
*/
getData() {
uni.showLoading({
title: "加载中",
});
let index = this.tabCurrentIndex;
getMemberCoupons(this.navList[index].params).then((res) => {
uni.stopPullDownRefresh();
if (res.data.success) {
let data = res.data.result.records;
if (data.length == 0) {
if (res.data.pageNumber == 1) {
this.navList[index].whetherEmpty = true;
} else {
this.navList[index].loadStatus = "noMore";
}
} else if (data.length < 10) {
this.navList[index].loadStatus = "noMore";
this.navList[index].dataList.push(...data);
} else {
this.navList[index].dataList.push(...data);
}
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
/**
* 切换tab
*/
changeTab(e) {
this.tabCurrentIndex = e.target.current;
},
/**
* 加载数据
*/
loadData() {
let index = this.tabCurrentIndex;
if (this.navList[index].loadStatus != "noMore") {
this.navList[index].params.pageNumber++;
this.getData();
}
},
/**
* 立即使用优惠券
*/
useItNow(item) {
uni.navigateTo({
url: `/pages/navigation/search/searchPage?promotionsId=${item.couponId}&promotionType=COUPON`,
});
},
/**
* 优惠券详情
*/
couponDetail(item) {
uni.navigateTo({
url:
"/pages/cart/coupon/couponDetail?item=" +
encodeURIComponent(JSON.stringify(item)),
});
},
},
};
</script>
<style lang="scss" scoped>
page {
height: 100%;
}
$item-color: #fff;
.b-content {
background: $page-color-base;
height: 100%;
}
.swiper-box {
height: calc(100vh - 40px);
}
.list-scroll-content {
height: 100%;
width: 100%;
.coupon-item {
display: flex;
align-items: center;
height: 220rpx;
margin: 20rpx;
.left {
height: 100%;
width: 260rpx;
background-color: $light-color;
position: relative;
.message {
color: $font-color-white;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 40rpx;
view:nth-child(1) {
font-weight: bold;
font-size: 60rpx;
}
view:nth-child(2) {
font-size: $font-sm;
}
}
.wave-line {
height: 220rpx;
width: 8rpx;
position: absolute;
top: 0;
left: 0;
background-color: $light-color;
overflow: hidden;
.wave {
width: 8rpx;
height: 16rpx;
background-color: #ffffff;
border-radius: 0 16rpx 16rpx 0;
margin-top: 4rpx;
}
}
.circle {
width: 40rpx;
height: 40rpx;
background-color: $bg-color;
position: absolute;
border-radius: 50%;
z-index: 111;
}
.circle-top {
top: -20rpx;
right: -20rpx;
}
.circle-bottom {
bottom: -20rpx;
right: -20rpx;
}
}
.right {
display: flex;
justify-content: space-between;
align-items: center;
width: 450rpx;
font-size: $font-sm;
height: 100%;
background-color: #ffffff;
overflow: hidden;
position: relative;
.content {
color: #666666;
margin-left: 20rpx;
line-height: 2em;
> view:nth-child(1) {
color: #ff6262;
font-size: 30rpx;
}
.title-1,
.title-2,
.title-3 {
font-size: 25rpx;
}
}
.receive {
color: #ffffff;
background-color: $main-color;
border-radius: 50%;
width: 86rpx;
height: 86rpx;
text-align: center;
margin-right: 48rpx;
vertical-align: middle;
padding-top: 8rpx;
position: relative;
z-index: 2;
}
.jiao-1 {
background-color: #ffc71c;
width: 400rpx;
transform: rotate(45deg);
text-align: center;
position: absolute;
color: #ffffff;
right: -130rpx;
top: 0;
.text-1 {
margin-left: 68rpx;
font-size: 28rpx;
}
.text-2 {
margin-left: 68rpx;
font-size: 28rpx;
}
}
.no-icon {
border-radius: 50%;
width: 86rpx;
height: 86rpx;
margin-right: 48rpx;
position: relative;
z-index: 2;
}
.bg-quan {
width: 244rpx;
height: 244rpx;
border: 6rpx solid $main-color;
border-radius: 50%;
opacity: 0.1;
color: $main-color;
text-align: center;
padding-top: 30rpx;
font-size: 130rpx;
position: absolute;
right: -54rpx;
bottom: -60rpx;
}
}
}
}
.navbar {
display: flex;
height: 80rpx;
padding: 0 5px;
background: #fff;
color: $light-color;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.06);
position: relative;
z-index: 10;
.nav-item {
flex: 1;
height: 100%;
font-size: 26rpx;
color: $light-color;
position: relative;
text-align: center;
text {
line-height: 80rpx;
}
.current {
font-weight: bold;
font-size: 28rpx;
&:after {
content: "";
position: absolute;
bottom: 10rpx;
left: 108rpx;
width: 30rpx;
border-bottom: 2px solid $light-color;
}
}
}
}
</style>
<template>
<view class="b-content">
<view class="coupon-tabs">
<u-tabs
:list="navList"
keyName="text"
:scrollable="false"
:inactiveStyle="{ color: '#333' }"
v-model:current="tabCurrentIndex"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
></u-tabs>
</view>
<swiper
:current="tabCurrentIndex"
class="swiper-box"
duration="300"
@change="changeTab"
>
<swiper-item
class="tab-content"
v-for="(navItem, navIndex) in navList"
:key="navIndex"
>
<scroll-view
class="list-scroll-content"
scroll-y
@scrolltolower="loadData"
>
<u-empty
mode="coupon"
text="暂无优惠券了"
v-if="navItem.whetherEmpty"
></u-empty>
<view
class="coupon-card"
:class="{ 'coupon-used': navIndex != 0, 'coupon-expired': navIndex == 2 }"
v-for="(coupon, index) in navItem.dataList"
:key="index"
>
<view class="coupon-card-left">
<text class="coupon-price-symbol" v-if="coupon.couponType != 'DISCOUNT'">¥</text>
<text class="coupon-price-value">{{ coupon.couponType == 'DISCOUNT' ? coupon.discount + '折' : unitPrice(coupon.price) }}</text>
</view>
<view class="coupon-divider"></view>
<view class="coupon-card-right">
<view class="coupon-info">
<text class="coupon-card-name">{{ coupon.title }}</text>
<text class="coupon-card-desc">使用范围{{
coupon.scopeType == "ALL" && coupon.storeId == "0"
? "全平台"
: coupon.scopeType == "PORTION_GOODS"
? "部分商品"
: coupon.scopeType == "PORTION_GOODS_CATEGORY"
? "部分分类商品"
: coupon.storeName == "platform"
? "全平台"
: coupon.storeName + ""
}}使用</text>
<text class="coupon-card-time" v-if="coupon.endTime">{{ coupon.endTime }}</text>
</view>
<view class="coupon-status-btn" v-if="navIndex == 0" @click="useItNow(coupon)">
立即使用
</view>
<view class="coupon-status-btn disabled" v-else-if="navIndex == 1">
已使用
</view>
<view class="coupon-status-btn disabled" v-else>
已过期
</view>
</view>
</view>
<uni-load-more :status="navItem.loadStatus"></uni-load-more>
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script setup lang="ts">
import { getMemberCoupons } from '@/api/members.js'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { onShow } from '@dcloudio/uni-app'
import { computed, ref, watch } from 'vue'
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const tabCurrentIndex = ref(0)
const navList = ref([
{
text: '未使用',
loadStatus: 'more',
dataList: [] as any[],
params: {
memberCouponStatus: 'NEW',
pageNumber: 1,
pageSize: 10,
status: 1,
},
whetherEmpty: false,
},
{
text: '已使用',
loadStatus: 'more',
dataList: [] as any[],
params: {
memberCouponStatus: 'USED',
pageNumber: 1,
pageSize: 10,
status: 2,
},
whetherEmpty: false,
},
{
text: '已过期',
loadStatus: 'more',
dataList: [] as any[],
params: {
memberCouponStatus: 'EXPIRE',
pageNumber: 1,
pageSize: 10,
status: 3,
},
whetherEmpty: false,
},
])
onShow(() => {
navList.value[tabCurrentIndex.value].params.pageNumber = 1
navList.value[tabCurrentIndex.value].dataList = []
getData()
})
watch(tabCurrentIndex, (val) => {
if (navList.value[val].dataList.length == 0) getData()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function handleTabClick(index: number) {
tabCurrentIndex.value = index
}
function getData() {
uni.showLoading({ title: '加载中' })
const index = tabCurrentIndex.value
getMemberCoupons(navList.value[index].params).then((res) => {
uni.stopPullDownRefresh()
if (res.data.success) {
const data = res.data.result.records
if (data.length == 0) {
if (res.data.pageNumber == 1) {
navList.value[index].whetherEmpty = true
} else {
navList.value[index].loadStatus = 'noMore'
}
} else if (data.length < 10) {
navList.value[index].loadStatus = 'noMore'
navList.value[index].dataList.push(...data)
} else {
navList.value[index].dataList.push(...data)
}
}
hideLoadingIfNeeded()
})
}
function changeTab(e: any) {
tabCurrentIndex.value = e.detail.current
}
function loadData() {
const index = tabCurrentIndex.value
if (navList.value[index].loadStatus != 'noMore') {
navList.value[index].params.pageNumber++
getData()
}
}
function useItNow(item: any) {
uni.navigateTo({
url: `/pages/navigation/search/searchPage?promotionsId=${item.couponId}&promotionType=COUPON`,
})
}
function couponDetail(item: any) {
uni.navigateTo({
url: '/pages/cart/coupon/couponDetail?item=' + encodeURIComponent(JSON.stringify(item)),
})
}
</script>
<style lang="scss" scoped>
.b-content {
background: #f7f8fa;
min-height: 100vh;
}
.coupon-tabs {
background: #fff;
height: 88rpx;
box-shadow: 0 1rpx 8rpx rgba(0, 0, 0, 0.04);
position: relative;
z-index: 2;
}
:deep(.u-tabs),
:deep(.u-tabs__wrapper),
:deep(.u-tabs__wrapper__scroll-view-wrapper),
:deep(.u-tabs__wrapper__scroll-view),
:deep(.u-tabs__wrapper__nav) {
background: #fff;
height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item) {
min-height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item__text) {
color: #333;
font-size: 28rpx;
}
.swiper-box {
height: calc(100vh - 88rpx - var(--status-bar-height));
}
.list-scroll-content {
height: 100%;
width: 100%;
padding: 24rpx;
box-sizing: border-box;
}
.coupon-card {
display: flex;
align-items: stretch;
background: #fff;
border-radius: 24rpx;
margin-bottom: 24rpx;
position: relative;
box-shadow: none;
border: 1rpx solid #f0f1f5;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
&:active {
transform: scale(0.98);
}
&::before,
&::after {
content: "";
position: absolute;
width: 32rpx;
height: 32rpx;
background: #f7f8fa;
border-radius: 50%;
left: 204rpx;
z-index: 2;
box-shadow: inset 0 0 0 1rpx #eceef2;
}
&::before {
top: -16rpx;
}
&::after {
bottom: -16rpx;
}
&.coupon-used {
.coupon-card-left {
background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
color: #999;
}
.coupon-status-btn {
background: #ccc;
box-shadow: none;
}
}
&.coupon-expired {
.coupon-card-left {
background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
color: #999;
}
.coupon-status-btn {
background: #ccc;
box-shadow: none;
}
}
}
.coupon-card-left {
width: 220rpx;
min-height: 180rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: #ff3b30;
position: relative;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
}
.coupon-price-symbol {
font-size: 32rpx;
font-weight: 700;
margin-right: 4rpx;
}
.coupon-price-value {
font-size: 56rpx;
font-weight: 900;
line-height: 1;
letter-spacing: -1rpx;
}
.coupon-divider {
position: absolute;
left: 220rpx;
top: 24rpx;
bottom: 24rpx;
width: 0;
border-left: 2rpx dashed #eceef2;
}
.coupon-card-right {
flex: 1;
padding: 32rpx 32rpx 32rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
background: #fff;
}
.coupon-info {
display: flex;
flex-direction: column;
gap: 12rpx;
flex: 1;
min-width: 0;
}
.coupon-card-name {
font-size: 30rpx;
color: #111;
font-weight: 700;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.coupon-card-desc {
font-size: 24rpx;
color: #8a8f99;
font-weight: 500;
}
.coupon-card-time {
font-size: 22rpx;
color: #b0b3bf;
}
.coupon-status-btn {
flex-shrink: 0;
padding: 16rpx 32rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, #ff6b35, #ff4b2b);
&:active {
opacity: 0.9;
}
&.disabled {
background: #ccc;
box-shadow: none;
}
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<view class="error-page">
<u-navbar title="支付失败" :border="false" :fixed="true" :placeholder="true" :auto-back="true"></u-navbar>
<view class="error-content">
<u-icon name="close-circle-fill" color="#f56c6c" size="120"></u-icon>
<text class="error-title">支付失败</text>
<text class="error-desc">{{ errorMessage || '支付过程中出现错误,请重试' }}</text>
<view class="btn-group">
<u-button type="primary" text="返回订单" @click="goBack"></u-button>
<u-button type="warning" text="去支付" @click="rePay"></u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const errorMessage = ref('')
const orderSn = ref('')
onLoad((options: any) => {
if (options) {
errorMessage.value = options.message || ''
orderSn.value = options.orderSn || ''
}
})
function goBack() {
uni.navigateBack({
fail: () => {
uni.switchTab({ url: '/pages/tabbar/user/my' })
}
})
}
function rePay() {
if (orderSn.value) {
uni.navigateTo({
url: `/pages/cart/payment/payOrder?orderSn=${orderSn.value}`
})
} else {
goBack()
}
}
</script>
<style lang="scss" scoped>
.error-page {
min-height: 100vh;
background: #fff;
}
.error-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 40rpx;
}
.error-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-top: 40rpx;
}
.error-desc {
font-size: 28rpx;
color: #999;
margin-top: 20rpx;
text-align: center;
}
.btn-group {
display: flex;
gap: 24rpx;
margin-top: 60rpx;
width: 100%;
:deep(.u-button) {
flex: 1;
}
}
</style>

View File

@@ -66,410 +66,264 @@
</div>
</div>
</template>
<script>
import * as API_Trade from "@/api/trade";
import {payCallback} from '@/api/members'
export default {
data() {
return {
//路径传参
routerVal: "",
//收银台参数
cashierParams: "",
//支付方式集合
payList: "",
//支付sn
sn: "",
//订单类型
orderType: "",
//支付异常
exception: {},
//支付表单
payForm: {},
//支付类型 APP/WECHAT_MP/H5/NATIVE app/微信小程序/h5/二维码
paymentType: "",
// 支付客户端 APP/NATIVE/JSAPI/H5
paymentClient: "",
//余额
walletValue: 0.0,
// 支付倒计时(毫秒)
autoCancelTime: 0,
};
},
onLoad(val) {
this.routerVal = val;
<script setup lang="ts">
import { ref, getCurrentInstance, onMounted } from 'vue'
import { onLoad, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Trade from '@/api/trade'
import { payCallback } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
//初始化参数
// #ifdef APP-PLUS
this.paymentType = "APP";
this.paymentClient = "APP";
//#endif
// #ifdef MP-WEIXIN
this.paymentType = "WECHAT_MP";
this.paymentClient = "MP";
//#endif
// #ifdef H5
this.paymentType = "H5";
//如果是微信浏览器则使用公众号支付否则使用h5
// 区别是h5是通过浏览器外部调用微信app进行支付而JSAPI则是 在微信浏览器内部,或者小程序 调用微信支付
this.paymentClient = this.isWeiXin() ? "JSAPI" : "H5";
//#endif
const store = useStore()
const { proxy } = getCurrentInstance()!
const routerVal = ref<Record<string, string>>({})
const cashierParams = ref<Record<string, any>>({ price: 0 })
const payList = ref<string[]>([])
const sn = ref('')
const orderType = ref('')
const paymentType = ref('')
const paymentClient = ref('')
const walletValue = ref(0)
const autoCancelTime = ref(0)
onLoad((val) => {
routerVal.value = val || {}
// #ifdef APP-PLUS
paymentType.value = 'APP'
paymentClient.value = 'APP'
// #endif
// #ifdef MP-WEIXIN
paymentType.value = 'WECHAT_MP'
paymentClient.value = 'MP'
// #endif
// #ifdef H5
paymentType.value = 'H5'
paymentClient.value = isWeiXin() ? 'JSAPI' : 'H5'
// #endif
})
//
},
onBackPress(e) {
if (e.from == "backbutton") {
if(this.routerVal.recharge_sn){
uni.switchTab({
url: '/pages/tabbar/user/my'
});
}
else{
uni.navigateTo({
url: "/pages/order/myOrder?status=0",
});
}
return true; //阻止默认返回行为
}
},
mounted() {
this.cashierData();
},
methods: {
onBackPress((e) => {
if (e.from == 'backbutton') {
if (routerVal.value.recharge_sn) {
uni.switchTab({ url: '/pages/tabbar/user/my' })
} else {
uni.navigateTo({ url: '/pages/order/myOrder?status=0' })
}
return true
}
return false
})
/**
* 支付成功后跳转
*/
callback(paymentMethod){
uni.navigateTo({
url: "/pages/cart/payment/success?paymentMethod=" +
paymentMethod +
"&payPrice=" +
this.cashierParams.price+
"&orderType="+this.orderType
});
},
/**
* 获取收银详情
*/
cashierData() {
let parms = {};
onMounted(() => {
cashierData()
})
if (this.routerVal.recharge_sn) {
// 判断当前是否是充值
this.sn = this.routerVal.recharge_sn;
this.orderType = "RECHARGE";
} else if (this.routerVal.trade_sn) {
this.sn = this.routerVal.trade_sn;
this.orderType = "TRADE";
} else {
this.sn = this.routerVal.order_sn;
this.orderType = "ORDER";
}
parms.sn = this.sn;
parms.orderType = this.orderType;
parms.clientType = this.paymentType;
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
API_Trade.getCashierData(parms).then((res) => {
if(res.data.success){
this.cashierParams = res.data.result;
function callback(paymentMethod: string) {
uni.navigateTo({
url:
'/pages/cart/payment/success?paymentMethod=' +
paymentMethod +
'&payPrice=' +
cashierParams.value.price +
'&orderType=' +
orderType.value,
})
}
// #ifdef MP-WEIXIN
this.payList = res.data.result.support.filter((item) => {
return item != "ALIPAY";
});
// #endif
function cashierData() {
const parms: Record<string, string> = {}
if(this.routerVal.recharge_sn){
this.payList = res.data.result.support.filter((item) => {
return item != "WALLET";
})
}
else{
this.payList = res.data.result.support;
}
// #ifdef H5
//判断是否微信浏览器
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
this.payList = res.data.result.support.filter((item) => {
return item != "ALIPAY";
});
// 充值的话仅保留微信支付
if(this.orderType == "RECHARGE"){
this.payList = res.data.result.support.filter((item) => {
return item == "WECHAT";
});
}
}
// #endif
if (routerVal.value.recharge_sn) {
sn.value = routerVal.value.recharge_sn
orderType.value = 'RECHARGE'
} else if (routerVal.value.trade_sn) {
sn.value = routerVal.value.trade_sn
orderType.value = 'TRADE'
} else {
sn.value = routerVal.value.order_sn
orderType.value = 'ORDER'
}
parms.sn = sn.value
parms.orderType = orderType.value
parms.clientType = paymentType.value
this.walletValue = res.data.result.walletValue;
const cancelAt = Number(res.data.result.autoCancel);
this.autoCancelTime = cancelAt > 0 ? Math.max(cancelAt - Date.now(), 0) : 0;
}
else if(res.data.code == 32000){
setTimeout(()=>{
uni.redirectTo({
url: `/pages/order/myOrder?status=0`
});
},500)
}
});
},
API_Trade.getCashierData(parms).then((res) => {
if (res.data.success) {
cashierParams.value = res.data.result
// #ifdef MP-WEIXIN
payList.value = res.data.result.support.filter((item: string) => item != 'ALIPAY')
// #endif
awaitPay(payment){
this.$u.throttle(()=>{
this.pay(payment)
}, 2000)
},
if (routerVal.value.recharge_sn) {
payList.value = res.data.result.support.filter((item: string) => item != 'WALLET')
} else {
payList.value = res.data.result.support
}
padTime(val) {
return String(val ?? 0).padStart(2, '0');
},
// #ifdef H5
const ua = window.navigator.userAgent.toLowerCase()
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
payList.value = res.data.result.support.filter((item: string) => item != 'ALIPAY')
if (orderType.value == 'RECHARGE') {
payList.value = res.data.result.support.filter((item: string) => item == 'WECHAT')
}
}
// #endif
onPayTimeout() {
uni.showToast({
title: '支付超时,请重新下单',
icon: 'none',
});
setTimeout(() => {
uni.redirectTo({
url: '/pages/order/myOrder?status=0',
});
}, 1500);
},
walletValue.value = res.data.result.walletValue
const cancelAt = Number(res.data.result.autoCancel)
autoCancelTime.value = cancelAt > 0 ? Math.max(cancelAt - Date.now(), 0) : 0
} else if (res.data.code == 32000) {
setTimeout(() => {
uni.redirectTo({ url: '/pages/order/myOrder?status=0' })
}, 500)
}
})
}
//订单支付
async pay(payment) {
// 支付编号
const sn = this.sn;
// 交易类型【交易号|订单号】
const orderType = this.orderType;
function awaitPay(payment: string) {
proxy.$u.throttle(() => {
pay(payment)
}, 2000)
}
const clientType = this.paymentType;
let params = {
sn,
orderType,
clientType,
};
function padTime(val: number) {
return String(val ?? 0).padStart(2, '0')
}
//支付方式 WECHAT/ALIPAY
const paymentMethod = payment;
// 客户端类型 APP/NATIVE/JSAPI/H5
const paymentClient = this.paymentClient;
uni.showLoading({
title: "正在唤起支付...",
mask:true
});
// #ifdef APP-PLUS
//APP pay
// 初始化支付签名
await API_Trade.initiatePay(paymentMethod, paymentClient, params).then(
(signXml) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
//如果支付异常
if (!signXml.data.success) {
uni.showToast({
title: signXml.data.message,
duration: 2000
});
return;
}
let payForm = signXml.data.result;
let paymentType = paymentMethod === "WECHAT" ? "wxpay" : "alipay";
if(paymentMethod === "WALLET"){
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
}
else{
uni.requestPayment({
provider: paymentType,
orderInfo: payForm || '',
success: (e) => {
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
},
fail: (e) => {
console.log(this);
this.exception = e;
uni.showModal({
content: "支付失败,如果您已支付,请勿反复支付",
showCancel: false,
});
},
});
}
}
);
//APP pay
// #endif
function onPayTimeout() {
uni.showToast({ title: '支付超时,请重新下单', icon: 'none' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/order/myOrder?status=0' })
}, 1500)
}
//#ifdef H5
//H5 pay
await API_Trade.initiatePay(paymentMethod, paymentClient, params).then(
(res) => {
let response = res.data;
//如果非支付宝支付才需要进行判定因为支付宝h5支付是直接输出的没有返回所谓的消息状态
if(paymentMethod !== "ALIPAY"){
//如果支付异常
if (!response.success) {
uni.showToast({
title: response.message,
duration: 2000,
icon:"none"
});
return;
}
}
if (paymentMethod === "ALIPAY") {
document.write(response);
} else if (paymentMethod === "WECHAT") {
if (this.isWeiXin()) {
//微信公众号支付
WeixinJSBridge.invoke(
"getBrandWCPayRequest",
response.result,
(res) => {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok但并不保证它绝对可靠。
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
} else {
uni.showModal({
content: "支付失败,如果您已支付,请勿反复支付",
showCancel: false,
});
}
}
);
if (this.$store.state.isShowToast){ uni.hideLoading() };
} else {
window.location.href = JSON.parse(response.result).h5_url;
const searchParams = {
...params,
price:this.cashierParams,
}
const timer = setInterval(()=>{
payCallback(searchParams).then(res=>{
if(res.data.result){
clearTimeout(timer);
uni.navigateTo({
url:"/pages/order/myOrder"
})
}
})
},3000)
if (this.$store.state.isShowToast){ uni.hideLoading() };
}
} else if (paymentMethod === "WALLET") {
uni.showToast({
title: response.message,
icon: "none",
});
if (response.success) {
this.callback(paymentMethod)
}
}
}
);
//H5pay
// #endif
function goPayError(message = '支付失败,如果您已支付,请勿反复支付') {
const query = [`orderSn=${encodeURIComponent(sn.value)}`, `message=${encodeURIComponent(message)}`]
uni.navigateTo({ url: `/pages/cart/payment/error?${query.join('&')}` })
}
//#ifdef MP-WEIXIN
//微信小程序
await API_Trade.initiatePay(paymentMethod, paymentClient, params).then(
(res) => {
let response = res.data.result;
//如果支付异常
if (!res.data.success) {
uni.showModal({
content: res.data.message,
showCancel: false,
})
return;
}
if (paymentMethod === "WECHAT") {
uni.requestPayment({
provider: "wxpay",
appid: response.appid,
timeStamp: response.timeStamp,
nonceStr: response.nonceStr,
package: response.package,
signType: response.signType,
paySign: response.paySign,
success: (e) => {
console.log(e);
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
},
fail: (e) => {
console.log(e);
this.exception = e;
uni.showModal({
content: "支付失败,如果您已支付,请勿反复支付",
showCancel: false,
});
},
});
} else {
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
}
}
);
// #endif
},
isWeiXin() {
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
return true;
} else {
return false;
}
},
},
};
async function pay(payment: string) {
const params = {
sn: sn.value,
orderType: orderType.value,
clientType: paymentType.value,
}
const paymentMethod = payment
const client = paymentClient.value
uni.showLoading({ title: '正在唤起支付...', mask: true })
// #ifdef APP-PLUS
await API_Trade.initiatePay(paymentMethod, client, params).then((signXml) => {
hideLoadingIfNeeded()
if (!signXml.data.success) {
uni.showToast({ title: signXml.data.message, duration: 2000 })
return
}
const payForm = signXml.data.result
const provider = paymentMethod === 'WECHAT' ? 'wxpay' : 'alipay'
if (paymentMethod === 'WALLET') {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
} else {
uni.requestPayment({
provider,
orderInfo: payForm || '',
success: () => {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
},
fail: () => {
goPayError()
},
})
}
})
// #endif
// #ifdef H5
await API_Trade.initiatePay(paymentMethod, client, params).then((res) => {
const response = res.data
if (paymentMethod !== 'ALIPAY' && !response.success) {
uni.showToast({ title: response.message, duration: 2000, icon: 'none' })
return
}
if (paymentMethod === 'ALIPAY') {
document.write(response)
} else if (paymentMethod === 'WECHAT') {
if (isWeiXin()) {
WeixinJSBridge.invoke('getBrandWCPayRequest', response.result, (payRes: any) => {
if (payRes.err_msg == 'get_brand_wcpay_request:ok') {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
} else {
goPayError()
}
})
hideLoadingIfNeeded()
} else {
window.location.href = JSON.parse(response.result).h5_url
const searchParams = { ...params, price: cashierParams.value }
const timer = setInterval(() => {
payCallback(searchParams).then((cbRes) => {
if (cbRes.data.result) {
clearInterval(timer)
uni.navigateTo({ url: '/pages/order/myOrder' })
}
})
}, 3000)
hideLoadingIfNeeded()
}
} else if (paymentMethod === 'WALLET') {
uni.showToast({ title: response.message, icon: 'none' })
if (response.success) callback(paymentMethod)
}
})
// #endif
// #ifdef MP-WEIXIN
await API_Trade.initiatePay(paymentMethod, client, params).then((res) => {
const response = res.data.result
if (!res.data.success) {
uni.showModal({ content: res.data.message, showCancel: false })
return
}
if (paymentMethod === 'WECHAT') {
uni.requestPayment({
provider: 'wxpay',
appid: response.appid,
timeStamp: response.timeStamp,
nonceStr: response.nonceStr,
package: response.package,
signType: response.signType,
paySign: response.paySign,
success: () => {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
},
fail: () => {
goPayError()
},
})
} else {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
}
})
// #endif
}
function isWeiXin() {
const ua = window.navigator.userAgent.toLowerCase()
return ua.match(/MicroMessenger/i) == 'micromessenger'
}
</script>
<style scoped lang="scss">
.method_icon {

View File

@@ -49,7 +49,13 @@
<!-- 倒计时 -->
<div class="count-down" v-if="!isOver && master.toBeGroupedNum">
<u-count-down bg-color="#ededed" :hide-zero-day="true" @end="isOver" :timestamp="timeStamp"></u-count-down>
<u-count-down
v-if="timeStamp > 0"
bgColor="#ededed"
:time="timeStamp * 1000"
format="HH:mm:ss"
@finish="onCountDownEnd"
></u-count-down>
</div>
<div class="user-list" v-if="data.pintuanMemberVOS">
@@ -59,173 +65,184 @@
</div>
</div>
<popupGoods :addr="addr" ref="popupGoods" :buyMask="maskFlag" @closeBuy="closePopupBuy" :goodsDetail="goodsDetail" :goodsSpec="goodsSpec" v-if="goodsDetail.id " @handleClickSku="getGoodsDetail" />
<shares @close="closeShare" :link="'/pages/cart/payment/shareOrderGoods?sn='+this.routers.sn+'&sku='+this.routers.sku+'&goodsId='+this.routers.goodsId" type="pintuan"
:thumbnail="data.promotionGoods.thumbnail" :goodsName="data.promotionGoods.goodsName" v-if="shareFlag " />
<popupGoods
:addr="addr"
ref="popupGoodsRef"
:buyMask="maskFlag"
@closeBuy="closePopupBuy"
:goodsDetail="goodsDetail"
:goodsSpec="goodsSpec"
v-if="goodsDetail.id"
@handleClickSku="getGoodsDetail"
/>
<shares
@close="closeShare"
:link="shareLink"
type="pintuan"
:thumbnail="data.promotionGoods.thumbnail"
:goodsName="data.promotionGoods.goodsName"
v-if="shareFlag"
/>
</view>
</template>
<script>
import { getGoods } from "@/api/goods.js";
import { getPinTuanShare } from "@/api/order";
import shares from "@/components/m-share/index";
import storage from "@/utils/storage.js";
import popupGoods from "@/components/m-buy/goods"; //购物车商品的模块
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getGoods } from '@/api/goods.js'
import { getPinTuanShare } from '@/api/order'
import shares from '@/components/m-share/index'
import storage from '@/utils/storage.js'
import popupGoods from '@/components/m-buy/goods'
import { unitPrice } from '@/utils/filters.js'
export default {
data() {
return {
flag: false, //判断接口是否正常请求
addr: {
id: "",
},
maskFlag: false, //商品弹框
timeStamp: 0,
shareFlag: false,
data: "",
isMaster: true,
selectedGoods: "", //选择的商品规格昵称
routers: "", //传参数据
goodsDetail: "", //商品详情
goodsSpec: "",
master: "", // 团长
PromotionList: "", //优惠集合
isGroup: false, //是否拼团
isOver: false, //是否结束活动
isBuy: false, //当前用户是是否购买
};
},
components: {
shares,
popupGoods,
},
watch: {
isGroup(val) {
if (val) {
let timer = setInterval(() => {
this.$refs.popupGoods.buyType = "PINTUAN";
clearInterval(timer);
}, 100);
} else {
this.$refs.popupGoods.buyType = "";
const store = useStore()
const popupGoodsRef = ref<any>(null)
const flag = ref(false)
const addr = ref({ id: '' })
const maskFlag = ref(false)
const timeStamp = ref(0)
const shareFlag = ref(false)
const data = ref<any>(null)
const isMaster = ref(true)
const selectedGoods = ref<any>(null)
const routers = ref<Record<string, string>>({})
const goodsDetail = ref<any>({})
const goodsSpec = ref<any>(null)
const master = ref<any>(null)
const PromotionList = ref<any>(null)
const isGroup = ref(false)
const isOver = ref(false)
const isBuy = ref(false)
const shareLink = computed(() => {
const { sn, sku, goodsId } = routers.value
return `/pages/cart/payment/shareOrderGoods?sn=${sn}&sku=${sku}&goodsId=${goodsId}`
})
watch(isGroup, (val) => {
if (val) {
const timer = setInterval(() => {
if (popupGoodsRef.value) {
popupGoodsRef.value.buyType = 'PINTUAN'
}
},
},
onLoad(options) {
this.routers = options;
},
mounted() {
this.init(this.routers.sn, this.routers.sku);
},
methods: {
closeShare() {
this.shareFlag = false;
},
// 这里的话得先跳到商品详情才能购买商品
toBuy() {
this.maskFlag = true;
this.$refs.popupGoods.parentOrder = {
...this.master,
orderSn: this.routers.sn,
};
this.$refs.popupGoods.isMask = true;
this.$refs.popupGoods.isClose = true;
this.$refs.popupGoods.buyType = "PINTUAN";
},
// 分享
share() {
this.shareFlag = true;
},
closePopupBuy(val) {
this.maskFlag = false;
},
// 实例化本页面
async init(sn, sku) {
let res = await getPinTuanShare(sn, sku);
if (res.data.success && res.data.result.promotionGoods) {
this.flag = true;
this.data = res.data.result;
this.selectedGoods = res.data.result.promotionGoods;
let endTime = Date.parse(
res.data.result.promotionGoods.endTime.replace(/-/g, "/")
);
// 获取当前剩余的拼团商品时间
let timeStamp = Date.parse(new Date(endTime)) / 1000;
clearInterval(timer)
}, 100)
} else if (popupGoodsRef.value) {
popupGoodsRef.value.buyType = ''
}
})
// 获取当前时间时间戳
let dateTime = Date.parse(new Date()) / 1000;
onLoad((options) => {
routers.value = options || {}
})
this.timeStamp = parseInt(timeStamp - dateTime);
onMounted(() => {
init(routers.value.sn, routers.value.sku)
})
this.timeStamp <= 0 ? (this.isOver = true) : (this.isOver = false);
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
// 获取剩余拼团人数
this.master =
res.data.result.pintuanMemberVOS.length != 0 &&
res.data.result.pintuanMemberVOS.filter((item) => {
return item.orderSn == "";
})[0];
function closeShare() {
shareFlag.value = false
}
// 获取当前是否是拼团本人
if (
storage.getUserInfo(this.routers.sku, this.routers.goodsId).id ==
this.master.memberId
) {
this.isMaster = true;
} else {
this.isMaster = false;
// 获取商品详情
this.getGoodsDetail({
id: this.routers.sku,
goodsId: this.routers.goodsId,
});
function onCountDownEnd() {
isOver.value = true
}
function toBuy() {
maskFlag.value = true
if (!popupGoodsRef.value) return
popupGoodsRef.value.parentOrder = {
...master.value,
orderSn: routers.value.sn,
}
popupGoodsRef.value.isMask = true
popupGoodsRef.value.isClose = true
popupGoodsRef.value.buyType = 'PINTUAN'
}
function share() {
shareFlag.value = true
}
function closePopupBuy() {
maskFlag.value = false
}
async function init(sn: string, sku: string) {
const res = await getPinTuanShare(sn, sku)
if (res.data.success && res.data.result.promotionGoods) {
flag.value = true
data.value = res.data.result
selectedGoods.value = res.data.result.promotionGoods
const endTime = Date.parse(
res.data.result.promotionGoods.endTime.replace(/-/g, '/')
)
const endTimestamp = Date.parse(new Date(endTime) as any) / 1000
const dateTime = Date.parse(new Date() as any) / 1000
timeStamp.value = parseInt(String(endTimestamp - dateTime))
isOver.value = timeStamp.value <= 0
master.value =
res.data.result.pintuanMemberVOS.length != 0 &&
res.data.result.pintuanMemberVOS.filter((item: any) => item.orderSn == '')[0]
if (
storage.getUserInfo(routers.value.sku, routers.value.goodsId).id ==
master.value.memberId
) {
isMaster.value = true
} else {
isMaster.value = false
getGoodsDetail({
id: routers.value.sku,
goodsId: routers.value.goodsId,
})
}
if (storage.getUserInfo().id) {
const bought = res.data.result.pintuanMemberVOS.filter(
(item: any) => item.memberId == storage.getUserInfo().id
)
isBuy.value = bought.length != 0
}
} else {
uni.showToast({
title: '当前拼团单有误!请联系管理员重试',
duration: 2000,
icon: 'none',
})
}
}
function getGoodsDetail(val: { id: string; goodsId: string }) {
const { id, goodsId } = val
uni.showLoading({ title: '加载中', mask: true })
getGoods(id, goodsId).then((response) => {
goodsDetail.value = response.data.result.data
selectedGoods.value = response.data.result.data
goodsSpec.value = response.data.result.specs
hideLoadingIfNeeded()
PromotionList.value = response.data.result.promotionMap
if (PromotionList.value) {
Object.keys(PromotionList.value).forEach((item) => {
if (item.indexOf('PINTUAN') == 0) {
isGroup.value = true
}
})
}
})
}
// 获取当前商品是否已经购买
if (storage.getUserInfo().id) {
let isBuy = res.data.result.pintuanMemberVOS.filter((item) => {
return item.memberId == storage.getUserInfo().id;
});
isBuy.length != 0 ? (this.isBuy = true) : (this.isBuy = false);
}
} else {
uni.showToast({
title: "当前拼团单有误!请联系管理员重试",
duration: 2000,
icon: "none",
});
}
},
// 获取商品详情
getGoodsDetail(val) {
let { id, goodsId } = val;
uni.showLoading({
title: "加载中",
mask: true,
});
getGoods(id, goodsId).then((response) => {
this.goodsDetail = response.data.result.data;
this.selectedGoods = response.data.result.data;
this.goodsSpec = response.data.result.specs;
if (this.$store.state.isShowToast){ uni.hideLoading() };
this.PromotionList = response.data.result.promotionMap;
// 判断是否拼团活动 如果有则显示拼团活动信息
this.PromotionList &&
Object.keys(this.PromotionList).forEach((item) => {
if (item.indexOf("PINTUAN") == 0) {
this.isGroup = true;
}
});
});
},
handleClickHome() {
uni.switchTab({
url: "/pages/tabbar/home/index",
});
},
},
};
function handleClickHome() {
uni.switchTab({ url: '/pages/tabbar/home/index' })
}
</script>
<style lang="scss" scoped>

View File

@@ -5,7 +5,7 @@
{{unitPrice(Number(payPrice)) }}
</div>
<div class="pay-btns">
<div v-show="!from" @click="checkOrder">查看{{ this.orderType == "RECHARGE" ? '余额' : '订单' }}</div>
<div v-show="!from" @click="checkOrder">查看{{ orderType == "RECHARGE" ? '余额' : '订单' }}</div>
<div @click="navigateTo('/pages/tabbar/home/index', 'switch')">回到首页</div>
</div>
</div>
@@ -24,73 +24,55 @@
</div>
</template>
<script>
import goodsRecommend from "@/components/m-goods-recommend";
export default {
data() {
return {
checked: false,
paymentMethod: "",
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import goodsRecommend from '@/components/m-goods-recommend'
import { unitPrice } from '@/utils/filters.js'
from: "",
payPrice: 0,
goodsList: [],
activeColor: this.$mainColor,
};
},
components: {
goodsRecommend,
},
onLoad(options) {
this.paymentMethod = options.paymentMethod || "";
this.from = options.from || "";
this.payPrice = options.payPrice || 0;
this.orderType = options.orderType;
const paymentMethod = ref('')
const from = ref('')
const payPrice = ref<number | string>(0)
const orderType = ref('')
},
methods: {
paymentTypeFilter(val) {
switch (val) {
case "WECHAT":
return "微信";
case "ALIPAY":
return "支付宝";
case "WALLET":
return "余额支付";
default:
return "";
}
},
checkOrder() {
/**
* 查看订单
* 1.充值跳转到明细里面
* 2.支付跳转到订单详情
*/
if (this.orderType == "RECHARGE") {
uni.reLaunch({
url: `/pages/mine/deposit/operation`,
});
} else {
this.navigateTo("/pages/order/myOrder?status=0");
}
},
onLoad((options) => {
paymentMethod.value = options.paymentMethod || ''
from.value = options.from || ''
payPrice.value = options.payPrice || 0
orderType.value = options.orderType || ''
})
navigateTo(url, type) {
if (type === "switch") {
uni.switchTab({
url,
});
} else {
uni.redirectTo({
url,
});
}
},
},
};
</script>
<style scoped lang="scss">
function paymentTypeFilter(val: string) {
switch (val) {
case 'WECHAT':
return '微信'
case 'ALIPAY':
return '支付宝'
case 'WALLET':
return '余额支付'
default:
return ''
}
}
function checkOrder() {
if (orderType.value == 'RECHARGE') {
uni.reLaunch({
url: '/pages/mine/deposit/operation',
})
} else {
navigateTo('/pages/order/myOrder?status=0')
}
}
function navigateTo(url: string, type?: string) {
if (type === 'switch') {
uni.switchTab({ url })
} else {
uni.redirectTo({ url })
}
}
</script><style scoped lang="scss">
.subscribe {
justify-content: space-between;
align-items: center;

View File

@@ -2,21 +2,16 @@
<default-page v-if="type" :type="type" title="您的设备已断网" :isBtn="false" />
</template>
<script>
import defaultPage from '@/components/default-page/default-page.vue';
export default {
components: {
defaultPage
},
data() {
return {
type: undefined
}
},
onLoad(options) {
}
}
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import defaultPage from '@/components/default-page/default-page.vue'
const type = ref<string>()
onLoad(() => {
type.value = 'msg'
})
</script>
<style>

View File

@@ -1,7 +1,14 @@
<template>
<view class="add-address">
<u-form :model="form" ref="uForm" error-type="toast" :rules="rules">
<u-form-item label="收货人" label-width="130" prop="name" :border-bottom="true">
<up-form
:model="form"
ref="uForm"
error-type="toast"
:rules="rules"
label-position="left"
label-width="180rpx"
>
<up-form-item label="收货人" label-width="180rpx" prop="name" :border-bottom="true">
<u-input
v-model="form.name"
border="none"
@@ -9,9 +16,9 @@
clearable
placeholder="请输入收货人姓名"
/>
</u-form-item>
</up-form-item>
<u-form-item label="手机号码" label-width="130" prop="mobile" :border-bottom="true">
<up-form-item label="手机号码" label-width="180rpx" prop="mobile" :border-bottom="true">
<u-input
v-model="form.mobile"
type="number"
@@ -20,18 +27,18 @@
input-align="right"
placeholder="请输入收货人手机号码"
/>
</u-form-item>
</up-form-item>
<u-form-item label="所在区域" label-width="130" prop="___path" :border-bottom="true">
<up-form-item label="所在区域" label-width="180rpx" prop="___path" :border-bottom="true">
<view class="form-value" @click="showPicker">
{{ form.___path || '请选择所在地区' }}
</view>
<template #right>
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</template>
</u-form-item>
</up-form-item>
<u-form-item class="detailAddress" label="详细地址" label-width="130" prop="detail" :border-bottom="true">
<up-form-item class="detailAddress" label="详细地址" label-width="180rpx" prop="detail" :border-bottom="true">
<u-input
type="textarea"
v-model="form.detail"
@@ -40,16 +47,16 @@
border="none"
placeholder="街道楼牌号等"
/>
</u-form-item>
</up-form-item>
<u-form-item label="地址别名" label-width="130" :border-bottom="false">
<up-form-item label="地址别名" label-width="180rpx" :border-bottom="false">
<u-input
v-model="form.alias"
border="none"
input-align="right"
placeholder="请输入地址别名"
/>
</u-form-item>
</up-form-item>
<view class="default-row">
<u-checkbox
@@ -63,7 +70,7 @@
</view>
<view class="saveBtn" @click="save">保存</view>
</u-form>
</up-form>
<m-city
:provinceData="list"
@@ -76,266 +83,208 @@
<uniMap v-if="mapFlag" @close="closeMap" @callback="callBackAddress" />
</view>
</template>
<script>
import { addAddress, editAddress, getAddressDetail } from "@/api/address.js";
import city from "@/components/m-city/m-city.vue";
import uniMap from "@/components/uniMap";
import permision from "@/js_sdk/wa-permission/permission.js";
export default {
components: {
"m-city": city,
uniMap,
<script setup lang="ts">
import { ref, reactive, computed, getCurrentInstance } from 'vue'
import { onLoad, onShow, onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { addAddress as addAddressApi, editAddress, getAddressDetail } from '@/api/address.js'
import MCity from '@/components/m-city/m-city.vue'
import uniMap from '@/components/uniMap'
import permision from '@/js_sdk/wa-permission/permission.js'
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const mapFlag = ref(false)
const routerVal = ref<Record<string, string>>({})
const uForm = ref<any>(null)
const cityPicker = ref<any>(null)
const form = reactive<Record<string, any>>({
detail: '',
name: '',
mobile: '',
consigneeAddressIdPath: [],
consigneeAddressPath: [],
___path: '',
isDefault: false,
})
const list = ref([
{
id: '',
localName: '请选择',
children: [],
},
onShow() {
// 判断当前系统权限定位是否开启
},
methods: {
// 关闭地图
closeMap() {
this.mapFlag = false;
])
const rules = {
name: [
{
required: true,
message: '收货人姓名不能为空',
trigger: ['blur', 'change'],
},
// 打开地图并访问权限
clickUniMap() {
// #ifdef APP-PLUS
if (plus.os.name == "iOS") {
// ios系统
permision.judgeIosPermission("location")
? (this.mapFlag = true)
: this.refuseMap();
} else {
// 安卓
this.requestAndroidPermission(
"android.permission.ACCESS_FINE_LOCATION"
);
}
// #endif
// #ifndef APP-PLUS
this.mapFlag = true;
// #endif
],
mobile: [
{
required: true,
message: '手机号码不能为空',
trigger: ['blur', 'change'],
},
// 如果拒绝权限 提示区设置
refuseMap() {
uni.showModal({
title: "温馨提示",
content: "您已拒绝定位,请开启",
confirmText: "去设置",
success(res) {
if (res.confirm) {
//打开授权设置
// #ifndef MP-WEIXIN
uni.getSystemInfo({
success(res) {
if (res.platform == "ios") {
//IOS
plus.runtime.openURL("app-settings://");
} else if (res.platform == "android") {
//安卓
let main = plus.android.runtimeMainActivity();
let Intent = plus.android.importClass(
"android.content.Intent"
);
let mIntent = new Intent("android.settings.ACTION_SETTINGS");
main.startActivity(mIntent);
}
},
});
// #endif
}
},
});
{
validator: (_rule: unknown, value: string) => proxy.$u.test.mobile(value),
message: '手机号码不正确',
trigger: ['change', 'blur'],
},
// 获取安卓是否拥有地址权限
async requestAndroidPermission(permisionID) {
var result = await permision.requestAndroidPermission(permisionID);
if (result == 1) {
this.mapFlag = true;
} else {
this.refuseMap();
}
],
___path: [
{
required: true,
message: '请选择所在区域',
trigger: ['change'],
},
// 选择地址后数据的回调
callBackAddress(val) {
console.log(val)
uni.showLoading({
title: "加载中",
});
if (val.regeocode && val) {
let address = val.regeocode;
this.form.detail = address.formatted_address; //地址详情
this.form.___path = val.data.result.name;
this.form.consigneeAddressIdPath = val.data.result.id; // 地址id分割
this.form.consigneeAddressPath = val.data.result.name; //地址名称, ''分割
this.form.lat = val.latitude; //纬度
this.form.lon = val.longitude; //经度
uni.hideLoading();
}
this.mapFlag = !this.mapFlag; //关闭地图
],
detail: [
{
required: true,
message: '请填写详细地址',
trigger: ['blur', 'change'],
},
],
}
// 保存当前 地址
save() {
this.$refs.uForm.validate().then(() => {
const params = { ...this.form };
delete params.___path;
onShow(() => {})
if (Array.isArray(params.consigneeAddressIdPath)) {
params.consigneeAddressIdPath = params.consigneeAddressIdPath.join(",");
}
if (Array.isArray(params.consigneeAddressPath)) {
params.consigneeAddressPath = params.consigneeAddressPath.join(",");
}
onLoad((option) => {
uni.showLoading({ title: '加载中' })
routerVal.value = option || {}
if (option.id) {
getAddressDetail(option.id).then((res) => {
const params = res.data.result
params.___path = params.consigneeAddressPath
Object.assign(form, params)
if (store.state.isShowToast) uni.hideLoading()
})
}
uni.hideLoading()
})
if (!params.id) {
addAddress(params).then((res) => {
if (res.data.success) {
uni.navigateBack();
} else {
uni.showToast({
title: res.data.message || "保存失败",
icon: "none",
});
onReady(() => {
uForm.value?.setRules(rules)
})
function closeMap() {
mapFlag.value = false
}
function refuseMap() {
uni.showModal({
title: '温馨提示',
content: '您已拒绝定位,请开启',
confirmText: '去设置',
success(res) {
if (res.confirm) {
// #ifndef MP-WEIXIN
uni.getSystemInfo({
success(sysRes) {
if (sysRes.platform == 'ios') {
plus.runtime.openURL('app-settings://')
} else if (sysRes.platform == 'android') {
const main = plus.android.runtimeMainActivity()
const Intent = plus.android.importClass('android.content.Intent')
const mIntent = new Intent('android.settings.ACTION_SETTINGS')
main.startActivity(mIntent)
}
});
} else {
delete params.updateBy;
delete params.updateTime;
editAddress(params).then((res) => {
if (res.data.success) {
uni.navigateBack();
} else {
uni.showToast({
title: res.data.message || "保存失败",
icon: "none",
});
}
});
}
}).catch(() => {});
},
})
// #endif
}
},
})
}
// 三级地址联动回调
getpickerParentValue(e) {
// 将需要绑定的地址设置为空,并赋值
this.form.consigneeAddressIdPath = [];
this.form.consigneeAddressPath = [];
let name = "";
async function requestAndroidPermission(permisionID: string) {
const result = await permision.requestAndroidPermission(permisionID)
if (result == 1) {
mapFlag.value = true
} else {
refuseMap()
}
}
e.forEach((item, index) => {
if (item.id) {
// 遍历数据
this.form.consigneeAddressIdPath.push(item.id);
this.form.consigneeAddressPath.push(item.localName);
name += item.localName;
this.form.___path = name;
}
if (index == e.length - 1) {
//如果是最后一个
let _town = item.children.filter((_child) => {
return _child.id == item.id;
});
function callBackAddress(val: any) {
uni.showLoading({ title: '加载中' })
if (val.regeocode && val) {
const address = val.regeocode
form.detail = address.formatted_address
form.___path = val.data.result.name
form.consigneeAddressIdPath = val.data.result.id
form.consigneeAddressPath = val.data.result.name
form.lat = val.latitude
form.lon = val.longitude
uni.hideLoading()
}
mapFlag.value = !mapFlag.value
}
this.form.lat = _town[0].center.split(",")[1];
this.form.lon = _town[0].center.split(",")[0];
}
});
},
function save() {
uForm.value?.validate().then(() => {
const params = { ...form }
delete params.___path
// 显示三级地址联动
showPicker() {
this.$refs.cityPicker.show();
},
},
mounted() {},
data() {
return {
lightColor: this.$lightColor, //高亮颜色
mapFlag: false, // 地图选择开
routerVal: "",
form: {
detail: "", //地址详情
name: "", //收货人姓名
mobile: "", //手机号码
consigneeAddressIdPath: [], //地址id
consigneeAddressPath: [], //地址名字
___path: "", //所在区域
isDefault: false, //是否默认地址
},
// 表单提交校验规则
rules: {
name: [
{
required: true,
message: "收货人姓名不能为空",
trigger: ["blur", "change"],
},
],
mobile: [
{
required: true,
message: "手机号码不能为空",
trigger: ["blur", "change"],
},
{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["change", "blur"],
},
],
___path: [
{
required: true,
message: "请选择所在区域",
trigger: ["change"],
},
],
detail: [
{
required: true,
message: "请填写详细地址",
trigger: ["blur", "change"],
},
],
},
list: [
{
id: "",
localName: "请选择",
children: [],
},
],
};
},
onLoad(option) {
uni.showLoading({
title: "加载中",
});
this.routerVal = option;
// 如果当前是编辑地址,则需要查询出地址详情信息
if (option.id) {
getAddressDetail(option.id).then((res) => {
const params = res.data.result;
params.___path = params.consigneeAddressPath;
this["form"] = params;
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
if (Array.isArray(params.consigneeAddressIdPath)) {
params.consigneeAddressIdPath = params.consigneeAddressIdPath.join(',')
}
uni.hideLoading();
},
// 初始化rules必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules);
},
};
if (Array.isArray(params.consigneeAddressPath)) {
params.consigneeAddressPath = params.consigneeAddressPath.join(',')
}
if (!params.id) {
addAddressApi(params).then((res) => {
if (res.data.success) {
uni.navigateBack()
} else {
uni.showToast({ title: res.data.message || '保存失败', icon: 'none' })
}
})
} else {
delete params.updateBy
delete params.updateTime
editAddress(params).then((res) => {
if (res.data.success) {
uni.navigateBack()
} else {
uni.showToast({ title: res.data.message || '保存失败', icon: 'none' })
}
})
}
}).catch(() => {})
}
function getpickerParentValue(e: any[]) {
form.consigneeAddressIdPath = []
form.consigneeAddressPath = []
let name = ''
e.forEach((item, index) => {
if (item.id) {
form.consigneeAddressIdPath.push(item.id)
form.consigneeAddressPath.push(item.localName)
name += item.localName
form.___path = name
}
if (index == e.length - 1) {
const _town = item.children.filter((_child: any) => _child.id == item.id)
form.lat = _town[0].center.split(',')[1]
form.lon = _town[0].center.split(',')[0]
}
})
}
function showPicker() {
cityPicker.value?.show()
}
</script>
<style scoped lang="scss">
page {

View File

@@ -42,126 +42,95 @@
</view>
</template>
<script>
import * as API_Trade from "@/api/trade";
import * as API_Address from "@/api/address.js";
export default {
data() {
return {
addressList: [], //地址列表
showAction: false, //是否显示下栏框
removeList: [
{
text: "确定",
},
],
tips: {
text: "确定要删除该收货人信息吗?",
},
removeId: "", //删除的地址id
routerVal: "",
params: {
pageNumber: 1,
pageSize: 1000,
},
};
},
onPullDownRefresh() {
//下拉刷新
this.addressList = [];
this.getAddressList();
},
onLoad: function (val) {
this.routerVal = val;
},
onShow() {
this.addressList = [];
this.getAddressList();
},
onHide() {},
methods: {
async selectAddressData(val) {
await API_Trade.setAddressId(val.id, this.routerVal.way);
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onShow, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Trade from '@/api/trade'
import * as API_Address from '@/api/address.js'
uni.navigateBack({
delta: 1,
});
},
//获取地址列表
getAddressList() {
uni.showLoading();
const store = useStore()
API_Address.getAddressList(
this.params.pageNumber,
this.params.pageSize
).then((res) => {
res.data.result.records.forEach((item) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(",");
});
this.addressList = res.data.result.records;
console.log(this.addressList);
const addressList = ref<any[]>([])
const showAction = ref(false)
const removeList = [{ text: '确定' }]
const tips = { text: '确定要删除该收货人信息吗?' }
const removeId = ref('')
const routerVal = ref<Record<string, string>>({})
const params = { pageNumber: 1, pageSize: 1000 }
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
//删除地址
removeAddress(id) {
this.removeId = id;
this.showAction = true;
},
deleteAddressMessage() {
API_Address.deleteAddress(this.removeId).then((res) => {
if (res.statusCode == 200) {
uni.showToast({
icon: "none",
title: "删除成功",
});
this.getAddressList();
} else {
uni.showToast({
icon: "none",
title: res.data.message,
duration: 2000,
});
}
});
},
//新建。编辑地址
addAddress(id) {
if (id) {
uni.navigateTo({
url:
"/pages/mine/address/add?id=" +
id +
"&way=" +
this.routerVal.way +
"&type=order",
});
} else {
uni.navigateTo({
url:
"/pages/mine/address/add?way=" + this.routerVal.way + "&type=order",
});
}
},
//设为默认地址
setDefault(item) {
delete item.updateBy;
delete item.updateTime;
delete item.deleteFlag;
onPullDownRefresh(() => {
addressList.value = []
getAddressList()
})
item.isDefault ? "" : (item.isDefault = !item.isDefault);
onLoad((val) => {
routerVal.value = val || {}
})
API_Address.editAddress(item).then((res) => {
uni.showToast({
title: "设置默认地址成功",
icon: "none",
});
this.getAddressList();
});
},
},
};
onShow(() => {
addressList.value = []
getAddressList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
async function selectAddressData(val: any) {
await API_Trade.setAddressId(val.id, routerVal.value.way)
uni.navigateBack({ delta: 1 })
}
function getAddressList() {
uni.showLoading()
API_Address.getAddressList(params.pageNumber, params.pageSize).then((res) => {
res.data.result.records.forEach((item: any) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(',')
})
addressList.value = res.data.result.records
hideLoadingIfNeeded()
})
}
function removeAddress(id: string) {
removeId.value = id
showAction.value = true
}
function deleteAddressMessage() {
API_Address.deleteAddress(removeId.value).then((res) => {
if (res.statusCode == 200) {
uni.showToast({ icon: 'none', title: '删除成功' })
getAddressList()
} else {
uni.showToast({ icon: 'none', title: res.data.message, duration: 2000 })
}
})
}
function addAddress(id: string) {
if (id) {
uni.navigateTo({
url: `/pages/mine/address/add?id=${id}&way=${routerVal.value.way}&type=order`,
})
} else {
uni.navigateTo({
url: `/pages/mine/address/add?way=${routerVal.value.way}&type=order`,
})
}
}
function setDefault(item: any) {
delete item.updateBy
delete item.updateTime
delete item.deleteFlag
if (!item.isDefault) item.isDefault = true
API_Address.editAddress(item).then(() => {
uni.showToast({ title: '设置默认地址成功', icon: 'none' })
getAddressList()
})
}
</script>
<style lang="scss" scoped>

View File

@@ -1,6 +1,6 @@
<template>
<view class="address">
<u-empty class="empty" v-if="this.addressList.length === 0" text="暂无收货地址" mode="address"></u-empty>
<u-empty class="empty" v-if="addressList.length === 0" text="暂无收货地址" mode="address"></u-empty>
<view class="list" >
<view class="item c-content" v-for="(item, index) in addressList" :key="index">
<view class="basic">
@@ -43,116 +43,90 @@
</view>
</template>
<script>
import * as API_Address from "@/api/address.js";
export default {
data() {
return {
addressList: [], //地址列表
showAction: false, //是否显示下栏框
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onShow, onPullDownRefresh, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { tipsToLogin } from '@/utils/filters.js'
import * as API_Address from '@/api/address.js'
removeList: [
{
text: "确定",
},
],
tips: {
text: "确定要删除该收货人信息吗?",
},
removeId: "", //删除的地址id
routerVal: "",
params: {
pageNumber: 1,
pageSize: 1000,
},
};
},
// 返回上一级
onBackPress(e) {
uni.switchTab({
url: "/pages/tabbar/user/my",
});
return true;
},
onLoad: function (val) {
this.routerVal = val;
},
onPullDownRefresh() {
//下拉刷新
this.addressList = [];
this.getAddressList();
},
/**
* 进入页面检测当前账户是否登录
*/
onShow() {
if (this.tipsToLogin()) {
this.getAddressList();
const store = useStore()
const addressList = ref<any[]>([])
const showAction = ref(false)
const removeList = [{ text: '确定' }]
const tips = { text: '确定要删除该收货人信息吗?' }
const removeId = ref('')
const routerVal = ref<Record<string, string>>({})
const params = { pageNumber: 1, pageSize: 1000 }
onBackPress(() => {
uni.switchTab({ url: '/pages/tabbar/user/my' })
return true
})
onLoad((val) => {
routerVal.value = val || {}
})
onPullDownRefresh(() => {
addressList.value = []
getAddressList()
})
onShow(() => {
if (tipsToLogin()) {
getAddressList()
}
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function getAddressList() {
uni.showLoading()
API_Address.getAddressList(params.pageNumber, params.pageSize).then((res) => {
res.data.result.records.forEach((item: any) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(',')
})
addressList.value = res.data.result.records
hideLoadingIfNeeded()
})
}
function removeAddress(id: string) {
removeId.value = id
showAction.value = true
}
function deleteAddressMessage() {
API_Address.deleteAddress(removeId.value).then((res) => {
if (res.statusCode == 200) {
uni.showToast({ icon: 'none', title: '删除成功' })
getAddressList()
} else {
uni.showToast({ icon: 'none', title: res.data.message, duration: 2000 })
}
},
methods: {
//获取地址列表
getAddressList() {
uni.showLoading();
API_Address.getAddressList(
this.params.pageNumber,
this.params.pageSize
).then((res) => {
res.data.result.records.forEach((item) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(",");
});
this.addressList = res.data.result.records;
})
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
//删除地址
removeAddress(id) {
this.removeId = id;
this.showAction = true;
},
// 删除地址
deleteAddressMessage() {
API_Address.deleteAddress(this.removeId).then((res) => {
if (res.statusCode == 200) {
uni.showToast({
icon: "none",
title: "删除成功",
});
this.getAddressList();
} else {
uni.showToast({
icon: "none",
title: res.data.message,
duration: 2000,
});
}
});
},
//新建。编辑地址
addAddress(id) {
uni.navigateTo({
url: `/pages/mine/address/add${id ? "?id=" + id : ""}`,
});
},
//设为默认地址
setDefault(item) {
delete item.updateBy;
delete item.updateTime;
delete item.deleteFlag;
function addAddress(id?: string) {
uni.navigateTo({
url: `/pages/mine/address/add${id ? '?id=' + id : ''}`,
})
}
item.isDefault ? "" : (item.isDefault = !item.isDefault);
API_Address.editAddress(item).then(() => {
uni.showToast({
title: "设置默认地址成功",
icon: "none",
});
this.getAddressList();
});
},
},
};
function setDefault(item: any) {
delete item.updateBy
delete item.updateTime
delete item.deleteFlag
if (!item.isDefault) item.isDefault = true
API_Address.editAddress(item).then(() => {
uni.showToast({ title: '设置默认地址成功', icon: 'none' })
getAddressList()
})
}
</script>
<style lang="scss" scoped>

View File

@@ -20,67 +20,57 @@
</view>
</template>
<script>
import * as API_Trade from "@/api/trade";
import * as API_Store from "@/api/store.js";
export default {
data() {
return {
storeAddressList: [], //地址列表
showAction: false, //是否显示下栏框
removeList: [
{
text: "确定",
},
],
tips: {
text: "确定要删除该收货人信息吗?",
},
removeId: "", //删除的地址id
routerVal: "",
params: {
pageNumber: 1,
pageSize: 1000,
},
};
},
onPullDownRefresh() {
//下拉刷新
this.storeAddressList = [];
this.getAddressList();
},
onLoad: function (val) {
this.routerVal = val;
},
onShow() {
this.storeAddressList = [];
this.getAddressList();
},
onHide() {},
methods: {
async selectAddressData(val) {
await API_Trade.setStoreAddressId(val.id, this.routerVal.way);
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onShow, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Trade from '@/api/trade'
import * as API_Store from '@/api/store.js'
uni.navigateBack({
delta: 1,
});
},
//获取地址列表
getAddressList() {
uni.showLoading();
const store = useStore()
API_Store.getStoreAddress(
this.routerVal.storeId,
this.params
).then((res) => {
this.storeAddressList = res.data.result.records;
console.log(this.storeAddressList);
const storeAddressList = ref<any[]>([])
const showAction = ref(false)
const removeList = [{ text: '确定' }]
const tips = { text: '确定要删除该收货人信息吗?' }
const removeId = ref('')
const routerVal = ref<Record<string, string>>({})
const params = { pageNumber: 1, pageSize: 1000 }
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
onPullDownRefresh(() => {
storeAddressList.value = []
getAddressList()
})
onLoad((val) => {
routerVal.value = val || {}
})
onShow(() => {
storeAddressList.value = []
getAddressList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
async function selectAddressData(val: any) {
await API_Trade.setStoreAddressId(val.id, routerVal.value.way)
uni.navigateBack({ delta: 1 })
}
function getAddressList() {
uni.showLoading()
API_Store.getStoreAddress(routerVal.value.storeId, params).then((res) => {
storeAddressList.value = res.data.result.records
hideLoadingIfNeeded()
})
}
function deleteAddressMessage() {
// 保留 action-sheet 回调占位,当前页面无删除入口
}
</script>
<style lang="scss" scoped>

View File

@@ -52,84 +52,64 @@
</view>
</template>
<script>
import { getUserRecharge, getWalletLog, getUserWallet } from "@/api/members";
export default {
data() {
return {
walletNum: 0,
current: 0,
swiperCurrent: 0,
userInfo: "",
params: {
pageNumber: 1,
pageSize: 10,
order: "desc",
},
depositData: [],
rechargeList: "",
walletLogList: "",
list: [{ name: "预存款变动明细" }],
};
},
watch: {
swiperCurrent(index) {
this.swiperCurrent = index;
},
},
async mounted() {
this.getWallet();
const result = await getUserWallet();
this.walletNum = result.data.result.memberWallet;
},
methods: {
isOutgoing(serviceType) {
return serviceType === "WALLET_PAY" || serviceType === "WALLET_WITHDRAWAL";
},
isIncoming(serviceType) {
return (
serviceType === "WALLET_REFUND" ||
serviceType === "WALLET_RECHARGE" ||
serviceType === "WALLET_COMMISSION"
);
},
getMoneyClass(serviceType) {
if (this.isOutgoing(serviceType)) return "out";
if (this.isIncoming(serviceType)) return "in";
return "";
},
formatLogMoney(logItem) {
const amount = this.unitPrice(logItem.money);
if (this.isOutgoing(logItem.serviceType)) return `-${amount}`;
if (this.isIncoming(logItem.serviceType)) return `+${amount}`;
return amount;
},
getRecharge() {
getUserRecharge(this.params).then((res) => {
if (res.data.success && res.data.result.records.length) {
this.depositData.push(...res.data.result.records);
}
});
},
getWallet() {
getWalletLog(this.params).then((res) => {
if (res.data.success && res.data.result.records.length) {
this.depositData.push(...res.data.result.records);
}
});
},
changed(index) {
this.depositData = [];
this.swiperCurrent = index;
this.params.pageNumber = 1;
this.getWallet();
},
loadMore() {
this.params.pageNumber++;
this.getWallet();
},
},
};
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getWalletLog, getUserWallet } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
const walletNum = ref(0)
const swiperCurrent = ref(0)
const params = ref({
pageNumber: 1,
pageSize: 10,
order: 'desc',
})
const depositData = ref<any[]>([])
const list = [{ name: '预存款变动明细' }]
onMounted(async () => {
getWallet()
const result = await getUserWallet()
walletNum.value = result.data.result.memberWallet
})
function isOutgoing(serviceType: string) {
return serviceType === 'WALLET_PAY' || serviceType === 'WALLET_WITHDRAWAL'
}
function isIncoming(serviceType: string) {
return (
serviceType === 'WALLET_REFUND' ||
serviceType === 'WALLET_RECHARGE' ||
serviceType === 'WALLET_COMMISSION'
)
}
function getMoneyClass(serviceType: string) {
if (isOutgoing(serviceType)) return 'out'
if (isIncoming(serviceType)) return 'in'
return ''
}
function formatLogMoney(logItem: any) {
const amount = unitPrice(logItem.money)
if (isOutgoing(logItem.serviceType)) return `-${amount}`
if (isIncoming(logItem.serviceType)) return `+${amount}`
return amount
}
function getWallet() {
getWalletLog(params.value).then((res) => {
if (res.data.success && res.data.result.records.length) {
depositData.value.push(...res.data.result.records)
}
})
}
function loadMore() {
params.value.pageNumber++
getWallet()
}
</script>
<style lang="scss" scoped>

View File

@@ -12,8 +12,7 @@
</view>
</template>
<script>
export default {};
<script setup lang="ts">
</script>
<style lang="scss" scoped>

View File

@@ -38,33 +38,30 @@
</view>
</template>
<script>
import { getUserWallet } from "@/api/members";
export default {
data() {
return {
walletNum: 0,
};
},
async onShow() {
if (this.isLogin("auth")) {
let result = await getUserWallet();
this.walletNum = result.data.result.memberWallet;
} else {
this.navigateToLogin("redirectTo");
}
},
methods: {
back() {
uni.switchTab({
url: "/pages/tabbar/user/my",
});
},
navigateTo(url) {
uni.navigateTo({ url });
},
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getUserWallet } from '@/api/members'
import { isLogin, navigateToLogin, unitPrice } from '@/utils/filters.js'
const walletNum = ref(0)
onShow(async () => {
if (isLogin('auth')) {
const result = await getUserWallet()
walletNum.value = result.data.result.memberWallet
} else {
navigateToLogin('redirectTo')
}
})
function back() {
uni.switchTab({ url: '/pages/tabbar/user/my' })
}
function navigateTo(url: string) {
uni.navigateTo({ url })
}
</script>
<style lang="scss" scoped>

View File

@@ -19,40 +19,30 @@
</view>
</template>
<script>
import { recharge } from "@/api/members";
export default {
data() {
return {
price: "",
flag: true,
};
},
watch: {
price(val) {
this.flag = !(Number(val) > 0);
},
},
methods: {
async handlerRecharge() {
const amount = Number(this.price);
if (!(amount > 0)) {
uni.showToast({
title: "请输入充值金额",
icon: "none",
});
return;
}
<script setup lang="ts">
import { ref, watch } from 'vue'
import { recharge } from '@/api/members'
const res = await recharge({ price: amount });
if (res.data.success) {
uni.navigateTo({
url: `/pages/cart/payment/payOrder?orderType=RECHARGE&recharge_sn=${res.data.result.rechargeSn}`,
});
}
},
},
};
const price = ref('')
const flag = ref(true)
watch(price, (val) => {
flag.value = !(Number(val) > 0)
})
async function handlerRecharge() {
const amount = Number(price.value)
if (!(amount > 0)) {
uni.showToast({ title: '请输入充值金额', icon: 'none' })
return
}
const res = await recharge({ price: amount })
if (res.data.success) {
uni.navigateTo({
url: `/pages/cart/payment/payOrder?orderType=RECHARGE&recharge_sn=${res.data.result.rechargeSn}`,
})
}
}
</script>
<style lang="scss" scoped>

View File

@@ -25,182 +25,149 @@
</view>
</template>
<script>
import { getWithdrawApplyPage, getWithdrawApplyWechatTransferInfo } from "@/api/members";
export default {
data() {
return {
loaded: false,
params: {
pageNumber: 1,
pageSize: 10,
order: "desc",
},
records: [],
};
},
onShow() {
this.params.pageNumber = 1;
this.records = [];
this.loaded = false;
this.getData();
},
methods: {
withdrawStatusText(applyStatus) {
switch (applyStatus) {
case "APPLY":
return "申请中";
case "VIA_AUDITING":
return "审核通过";
case "D_VIA_AUDITING":
return "分销提现审核通过";
case "FAIL_AUDITING":
return "审核未通过";
case "D_FAIL_AUDITING":
return "分销提现审核未通过";
case "WAIT_USER_CONFIRM":
return "等待用户确认";
case "SUCCESS":
return "提现成功";
case "ERROR":
return "提现失败";
default:
return applyStatus || "";
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getWithdrawApplyPage, getWithdrawApplyWechatTransferInfo } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
const loaded = ref(false)
const params = ref({
pageNumber: 1,
pageSize: 10,
order: 'desc',
})
const records = ref<any[]>([])
onShow(() => {
params.value.pageNumber = 1
records.value = []
loaded.value = false
getData()
})
function withdrawStatusText(applyStatus: string) {
switch (applyStatus) {
case 'APPLY':
return '申请中'
case 'VIA_AUDITING':
return '审核通过'
case 'D_VIA_AUDITING':
return '分销提现审核通过'
case 'FAIL_AUDITING':
return '审核未通过'
case 'D_FAIL_AUDITING':
return '分销提现审核未通过'
case 'WAIT_USER_CONFIRM':
return '等待用户确认'
case 'SUCCESS':
return '提现成功'
case 'ERROR':
return '提现失败'
default:
return applyStatus || ''
}
}
function getData() {
getWithdrawApplyPage(params.value).then((res) => {
loaded.value = true
if (res.data.success && res.data.result.records.length != 0) {
records.value.push(...res.data.result.records)
}
})
}
function loadMore() {
params.value.pageNumber++
getData()
}
function confirmWechatReceive(item: any) {
const id = item && item.id
if (!id) {
uni.showToast({ title: '缺少提现记录ID', duration: 2000, icon: 'none' })
return
}
uni.showLoading({ title: '加载中' })
getWithdrawApplyWechatTransferInfo(id)
.then((res) => {
if (!res.data || !res.data.success) return
const info = res.data.result || {}
const mchId = info.mchId
const wechatPackage = info.wechatPackage
let appId = info.appId
if (typeof wx !== 'undefined' && wx.getAccountInfoSync) {
try {
const accountInfo = wx.getAccountInfoSync()
const mpAppId = accountInfo && accountInfo.miniProgram && accountInfo.miniProgram.appId
if (mpAppId) appId = mpAppId
} catch (e) {}
}
},
getData() {
getWithdrawApplyPage(this.params).then((res) => {
this.loaded = true;
if (res.data.success) {
if (res.data.result.records.length != 0) {
this.records.push(...res.data.result.records);
}
if (!mchId || !appId || !wechatPackage) {
uni.showToast({ title: '微信确认参数缺失', duration: 2000, icon: 'none' })
return
}
const openResultToast = (errMsg?: string) => {
if (errMsg === 'requestMerchantTransfer:ok') {
uni.showToast({ title: '已唤起确认页面', duration: 2000, icon: 'none' })
return
}
if (errMsg === 'requestMerchantTransfer:cancel') {
uni.showToast({ title: '已取消', duration: 2000, icon: 'none' })
return
}
});
},
loadMore() {
this.params.pageNumber++;
this.getData();
},
confirmWechatReceive(item) {
const id = item && item.id;
if (!id) {
uni.showToast({
title: "缺少提现记录ID",
duration: 2000,
icon: "none",
});
return;
title: errMsg ? `唤起失败:${errMsg}` : '唤起失败',
duration: 2500,
icon: 'none',
})
}
uni.showLoading({
title: "加载中",
});
getWithdrawApplyWechatTransferInfo(id)
.then((res) => {
if (!res.data || !res.data.success) return;
const info = res.data.result || {};
const mchId = info.mchId;
const wechatPackage = info.wechatPackage;
let appId = info.appId;
if (typeof wx !== "undefined" && wx.getAccountInfoSync) {
try {
const accountInfo = wx.getAccountInfoSync();
const mpAppId = accountInfo && accountInfo.miniProgram && accountInfo.miniProgram.appId;
if (mpAppId) appId = mpAppId;
} catch (e) {}
}
if (!mchId || !appId || !wechatPackage) {
if (typeof wx !== 'undefined' && wx.getSystemInfoSync) {
try {
const sys = wx.getSystemInfoSync()
if (sys && sys.platform === 'devtools') {
uni.showToast({
title: "微信确认参数缺失",
duration: 2000,
icon: "none",
});
return;
}
const openResultToast = (errMsg) => {
if (errMsg === "requestMerchantTransfer:ok") {
uni.showToast({
title: "已唤起确认页面",
duration: 2000,
icon: "none",
});
return;
}
if (errMsg === "requestMerchantTransfer:cancel") {
uni.showToast({
title: "已取消",
duration: 2000,
icon: "none",
});
return;
}
uni.showToast({
title: errMsg ? `唤起失败:${errMsg}` : "唤起失败",
title: '开发者工具可能不支持,请真机测试',
duration: 2500,
icon: "none",
});
};
if (typeof wx !== "undefined" && wx.getSystemInfoSync) {
try {
const sys = wx.getSystemInfoSync();
if (sys && sys.platform === "devtools") {
uni.showToast({
title: "开发者工具可能不支持,请真机测试",
duration: 2500,
icon: "none",
});
}
} catch (e) {}
icon: 'none',
})
}
} catch (e) {}
}
if (typeof wx !== "undefined" && wx.canIUse && wx.canIUse("requestMerchantTransfer")) {
wx.requestMerchantTransfer({
mchId,
appId,
package: wechatPackage,
success: (r) => {
openResultToast(r && (r.errMsg || r.err_msg));
},
fail: (r) => {
openResultToast(r && (r.errMsg || r.err_msg));
},
});
return;
}
if (typeof WeixinJSBridge !== "undefined" && WeixinJSBridge.invoke) {
WeixinJSBridge.invoke(
"requestMerchantTransfer",
{
mchId,
appId,
package: wechatPackage,
},
(r) => {
openResultToast(r && (r.errMsg || r.err_msg));
}
);
return;
}
uni.showToast({
title: "请在微信内打开确认收款",
duration: 2000,
icon: "none",
});
if (typeof wx !== 'undefined' && wx.canIUse && wx.canIUse('requestMerchantTransfer')) {
wx.requestMerchantTransfer({
mchId,
appId,
package: wechatPackage,
success: (r: any) => openResultToast(r && (r.errMsg || r.err_msg)),
fail: (r: any) => openResultToast(r && (r.errMsg || r.err_msg)),
})
.finally(() => {
uni.hideLoading();
});
},
},
};
return
}
if (typeof WeixinJSBridge !== 'undefined' && WeixinJSBridge.invoke) {
WeixinJSBridge.invoke(
'requestMerchantTransfer',
{ mchId, appId, package: wechatPackage },
(r: any) => openResultToast(r && (r.errMsg || r.err_msg))
)
return
}
uni.showToast({ title: '请在微信内打开确认收款', duration: 2000, icon: 'none' })
})
.finally(() => {
uni.hideLoading()
})
}
</script>
<style lang="scss" scoped>

View File

@@ -58,77 +58,68 @@
</view>
</template>
<script>
import { getUserWallet, withdrawalApply, withdrawalSettingVO } from "@/api/members";
export default {
data() {
return {
price: "",
walletNum: 0,
minPrice: 0,
type: "",
connectNumber: "",
realName: "",
};
},
computed: {
typeLabel() {
if (this.type === "ALI") return "支付宝";
if (this.type) return "微信";
return "--";
},
},
async mounted() {
const result = await getUserWallet();
const res = await withdrawalSettingVO();
this.walletNum = result.data.result.memberWallet;
this.minPrice = res.data.result.minPrice;
this.type = res.data.result.type;
},
methods: {
cashd() {
const amount = Number(this.price);
if (!this.$u.test.amount(parseInt(amount))) {
uni.showToast({
title: "请输入正确金额",
duration: 2000,
icon: "none",
});
return;
}
<script setup lang="ts">
import { ref, computed, getCurrentInstance, onMounted } from 'vue'
import { getUserWallet, withdrawalApply, withdrawalSettingVO } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
const params = { price: amount };
if (this.type === "ALI") {
if (!this.connectNumber || !this.realName) {
uni.showToast({
title: "请输入真实姓名和第三方登录账号",
duration: 2000,
icon: "none",
});
return;
}
params.connectNumber = this.connectNumber;
params.realName = this.realName;
}
const { proxy } = getCurrentInstance()!
withdrawalApply(params).then((res) => {
if (res.data.success) {
uni.showToast({
title: "提现成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({ delta: 1 });
}, 1000);
}
});
},
handleAll() {
this.price = String(this.walletNum || "");
},
},
};
const price = ref('')
const walletNum = ref(0)
const minPrice = ref(0)
const type = ref('')
const connectNumber = ref('')
const realName = ref('')
const typeLabel = computed(() => {
if (type.value === 'ALI') return '支付宝'
if (type.value) return '微信'
return '--'
})
onMounted(async () => {
const result = await getUserWallet()
const res = await withdrawalSettingVO()
walletNum.value = result.data.result.memberWallet
minPrice.value = res.data.result.minPrice
type.value = res.data.result.type
})
function cashd() {
const amount = Number(price.value)
if (!proxy.$u.test.amount(parseInt(String(amount)))) {
uni.showToast({ title: '请输入正确金额', duration: 2000, icon: 'none' })
return
}
const params: Record<string, any> = { price: amount }
if (type.value === 'ALI') {
if (!connectNumber.value || !realName.value) {
uni.showToast({
title: '请输入真实姓名和第三方登录账号',
duration: 2000,
icon: 'none',
})
return
}
params.connectNumber = connectNumber.value
params.realName = realName.value
}
withdrawalApply(params).then((res) => {
if (res.data.success) {
uni.showToast({ title: '提现成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
})
}
function handleAll() {
price.value = String(walletNum.value || '')
}
</script>
<style lang="scss" scoped>

View File

@@ -1,11 +1,10 @@
<template>
<view></view>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>
<script setup lang="ts">
// 占位页,暂无业务逻辑
</script>
<style lang="scss" scoped>
</style>

View File

@@ -3,160 +3,100 @@
<view>
<h4>实名认证请上传真实的个人信息认证通过后将无法修改</h4>
<view>
<u-form :model="ruleForm" label-width="200rpx" ref="uForm">
<u-form-item label="姓名" prop="name">
<u-input v-model="ruleForm.name" placeholder="请输入您的真实姓名" />
</u-form-item>
<u-form-item label="身份证" prop="idNumber">
<u-input v-model="ruleForm.idNumber" placeholder="请输入身份证号码" />
</u-form-item>
<u-form-item label="银行开户行" prop="settlementBankBranchName">
<u-input v-model="ruleForm.settlementBankBranchName" type="text" placeholder="请输入银行开户行" />
</u-form-item>
<u-form-item label="银行开户名" prop="settlementBankAccountName">
<u-input v-model="ruleForm.settlementBankAccountName" type="text" placeholder="请输入银行开户名" />
</u-form-item>
<u-form-item label="银行账号" prop="settlementBankAccountNum">
<u-input v-model="ruleForm.settlementBankAccountNum" type="text" placeholder="请输入银行账号" />
</u-form-item>
<!-- <u-form-item label="身份证正面照" prop="name">
<u-upload></u-upload>
</u-form-item>
<u-form-item label="身份证反面照" prop="name">
<u-upload></u-upload>
</u-form-item>
<u-form-item label="手持身份证照" prop="name">
<u-upload></u-upload>
</u-form-item> -->
</u-form>
<u-button :customStyle="{'background':$lightColor,'color':'#fff' }" @click="submit">提交</u-button>
<up-form
:model="formData"
label-position="left"
label-width="180rpx"
ref="uFormRef"
>
<up-form-item label="姓名" prop="name">
<u-input v-model="formData.name" placeholder="请输入您的真实姓名" />
</up-form-item>
<up-form-item label="身份证" prop="idNumber">
<u-input v-model="formData.idNumber" placeholder="请输入身份证号码" />
</up-form-item>
<up-form-item label="银行开户行" prop="settlementBankBranchName">
<u-input v-model="formData.settlementBankBranchName" placeholder="请输入银行开户行" />
</up-form-item>
<up-form-item label="银行开户名" prop="settlementBankAccountName">
<u-input v-model="formData.settlementBankAccountName" placeholder="请输入银行开户名" />
</up-form-item>
<up-form-item label="银行账号" prop="settlementBankAccountNum">
<u-input v-model="formData.settlementBankAccountNum" placeholder="请输入银行账号" />
</up-form-item>
</up-form>
<u-button :customStyle="{ background: lightColor, color: '#fff' }" @click="submitForm">提交</u-button>
</view>
</view>
<view class="tips">
<view>您提交的信息正在审核</view>
<view>提交认证申请后工作人员将在三个工作日进行核对完成审核</view>
</view>
</view>
</template>
<script>
import { applyDistribution } from "@/api/goods";
export default {
data() {
return {
ruleForm: {
name: "",
idNumber: "",
settlementBankBranchName: "", // 银行开户行
settlementBankAccountName: "", //银行开户名
settlementBankAccountNum: "", //银行账号
},
rules: {
name: [
{
required: true,
message: "请输入姓名",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.chinese(value);
},
message: "姓名输入不正确",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
},
],
// 银行开户行
settlementBankBranchName: [
{
required: true,
message: "请输入银行开户行",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
], //银行开户名
settlementBankAccountName: [
{
required: true,
message: "银行开户名",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
],
//银行账号
settlementBankAccountNum: [
{
required: true,
message: "请输入银行账号",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
],
idNumber: [
{
required: true,
message: "请输入身份证",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.idCard(value);
},
message: "身份证号码不正确",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
},
],
},
};
},
methods: {
submit() {
this.$refs.uForm.validate((valid) => {
if (valid) {
applyDistribution(this.ruleForm).then((res) => {
if (res.data.success) {
uni.showToast({
title: "认证提交成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack();
}, 500);
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
} else {
uni.showToast({
title: "请填写有效信息",
duration: 2000,
icon: "none",
});
}
});
<script setup lang="ts">
import { reactive, computed, ref, getCurrentInstance } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { applyDistribution } from '@/api/goods'
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const uFormRef = ref<any>(null)
const formData = reactive({
name: '',
idNumber: '',
settlementBankBranchName: '',
settlementBankAccountName: '',
settlementBankAccountNum: '',
})
const rules = {
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{
validator: (_rule: unknown, value: string) => proxy.$u.test.chinese(value),
message: '姓名输入不正确',
trigger: ['change', 'blur'],
},
},
onReady() {
this.$refs.uForm.setRules(this.rules);
},
};
],
settlementBankBranchName: [{ required: true, message: '请输入银行开户行', trigger: 'blur' }],
settlementBankAccountName: [{ required: true, message: '银行开户名', trigger: 'blur' }],
settlementBankAccountNum: [{ required: true, message: '请输入银行账号', trigger: 'blur' }],
idNumber: [
{ required: true, message: '请输入身份证', trigger: 'blur' },
{
validator: (_rule: unknown, value: string) => proxy.$u.test.idCard(value),
message: '身份证号码不正确',
trigger: ['change', 'blur'],
},
],
}
onReady(() => {
uFormRef.value?.setRules(rules)
})
function submitForm() {
uFormRef.value?.validate().then(() => {
applyDistribution(formData).then((res) => {
if (res.data.success) {
uni.showToast({ title: '认证提交成功!', duration: 2000, icon: 'none' })
setTimeout(() => uni.navigateBack(), 500)
} else {
uni.showToast({ title: res.data.message, duration: 2000, icon: 'none' })
}
})
}).catch(() => {
uni.showToast({ title: '请填写有效信息', duration: 2000, icon: 'none' })
})
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 32rpx;

View File

@@ -1,30 +1,38 @@
<template>
<view class="log-list">
<!-- 提现记录 -->
<view class="log-way" v-if="cashLogData.length != 0" v-for="(item, index) in cashLogData" :key="index">
<view
class="log-way"
v-if="withdrawLogList.length != 0"
v-for="(item, index) in withdrawLogList"
:key="'cash-' + index"
>
<view class="log-item">
<view class="log-item-view">
<view class="title">{{
item.distributionCashStatus == "APPLY"
? "待处理"
: item.distributionCashStatus == "VIA_AUDITING"
? "通过"
: "拒绝"
item.distributionCashStatus == 'APPLY'
? '待处理'
: item.distributionCashStatus == 'VIA_AUDITING'
? '通过'
: '拒绝'
}}</view>
<view class="price">+{{unitPrice(item.price) }}</view>
<view class="price">+{{ unitPrice(item.price) }}</view>
</view>
<view class="log-item-view">
<view>{{ item.createTime }}</view>
<view></view>
</view>
</view>
</view>
<!-- 分销业绩 -->
<view class="log-way" v-if="achievementData.length != 0" v-for="(item, index) in achievementData" :key="index">
<view
class="log-way"
v-if="achievementList.length != 0"
v-for="(item, index) in achievementList"
:key="'ach-' + index"
>
<view class="log-item">
<view class="log-item-view">
<view class="title">{{ item.goodsName }}</view>
<view class="price">提成金额+{{unitPrice(item.rebate) }}</view>
<view class="price">提成金额+{{ unitPrice(item.rebate) }}</view>
</view>
<view class="log-item-view">
<view>创建时间{{ item.createTime }}</view>
@@ -38,101 +46,98 @@
</view>
</view>
</view>
<view class="empty" v-if="empty">
<u-loadmore :status="status" :icon-type="iconType" bg-color="#f7f7f7" />
<view class="empty" v-if="isEmpty">
<u-loadmore :status="loadStatus" :icon-type="iconType" bg-color="#f7f7f7" />
</view>
</view>
</template>
<script>
import { cashLog, distributionOrderList } from "@/api/goods";
export default {
data () {
return {
cashLogData: [], //提现记录数据集合
achievementData: [], //分销业绩数据合集,
status: "loadmore",
iconType: "flower",
empty: false,
params: {
pageNumber: 1,
pageSize: 10,
},
type: 0,
routers: "",
achParams: {
pageNumber: 1,
pageSize: 10,
},
};
},
onLoad (option) {
let title;
option.type == 0 ? (title = "分销业绩") : (title = "提现记录");
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { cashLog, distributionOrderList } from '@/api/goods'
import { unitPrice } from '@/utils/filters.js'
uni.setNavigationBarTitle({
title: title, //这是修改后的导航栏文字
});
this.routers = option;
this.type = option.type;
option.type == 0 ? this.achievement() : this.history();
},
mounted () { },
onReachBottom () {
this.status = "loading";
this.type == 0 ? this.achParams.pageNumber++ : this.params.pageNumber++;
this.type == 0 ? this.achievement() : this.history();
},
methods: {
// 业绩
achievement () {
uni.showLoading({
title: "加载中",
});
distributionOrderList(this.achParams).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
this.achievementData.push(...res.data.result.records);
} else {
this.status = "nomore";
this.empty = true;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
// 初始化提现历史
history () {
uni.showLoading({
title: "加载中",
});
cashLog(this.params).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
this.cashLogData.push(...res.data.result.records);
} else {
this.status = "nomore";
this.empty = true;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
const store = useStore()
const withdrawLogList = ref<any[]>([])
const achievementList = ref<any[]>([])
const loadStatus = ref('loadmore')
const iconType = ref('flower')
const isEmpty = ref(false)
const listType = ref(0)
const routeQuery = ref<Record<string, string>>({})
const withdrawParams = ref({ pageNumber: 1, pageSize: 10 })
const achievementParams = ref({ pageNumber: 1, pageSize: 10 })
onLoad((option) => {
const type = Number(option.type ?? 0)
listType.value = type
routeQuery.value = option || {}
uni.setNavigationBarTitle({
title: type === 0 ? '分销业绩' : '提现记录',
})
type === 0 ? fetchAchievementList() : fetchWithdrawLog()
})
onReachBottom(() => {
loadStatus.value = 'loading'
if (listType.value === 0) {
achievementParams.value.pageNumber++
fetchAchievementList()
} else {
withdrawParams.value.pageNumber++
fetchWithdrawLog()
}
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function fetchAchievementList() {
uni.showLoading({ title: '加载中' })
distributionOrderList(achievementParams.value).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
achievementList.value.push(...res.data.result.records)
} else {
loadStatus.value = 'nomore'
isEmpty.value = true
}
hideLoadingIfNeeded()
})
}
function fetchWithdrawLog() {
uni.showLoading({ title: '加载中' })
cashLog(withdrawParams.value).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
withdrawLogList.value.push(...res.data.result.records)
} else {
loadStatus.value = 'nomore'
isEmpty.value = true
}
hideLoadingIfNeeded()
})
}
</script>
<style lang="scss" scoped>
.empty {
margin: 40rpx 0;
}
.price {
color: $main-color;
font-weight: bold;
}
.log-list {
padding: 0 8rpx;
overflow: hidden;
margin: 20rpx 0;
}
.log-way {
margin: 10rpx 0;
overflow: hidden;
@@ -140,26 +145,17 @@ export default {
border-radius: 10rpx;
padding: 20rpx 0;
}
.title {
font-size: 30rpx;
font-weight: bold;
}
.log-item-view {
padding: 8rpx 32rpx;
display: flex;
font-size: 13px;
justify-content: space-between;
}
.log-item-footer {
padding: 8rpx 32rpx;
display: flex;
font-size: 13px;
justify-content: space-between;
}
.log-item-footer,
.log-item-footers {
padding: 8rpx 32rpx;
display: flex;

View File

@@ -1,18 +1,15 @@
<template>
<view>
<view class="nav-list">
<view class="total">可提现金额</view>
<view class="price">{{unitPrice(distributionData.canRebate) }}</view>
<view class="frozen"
>冻结金额{{unitPrice(distributionData.commissionFrozen) }}</view
>
<view class="price">{{ unitPrice(distributionData.canRebate) }}</view>
<view class="frozen">冻结金额{{ unitPrice(distributionData.commissionFrozen) }}</view>
</view>
<view class="nav">
<view class="nav-item">
<u-icon
size="50"
@click="handleClick('/pages/mine/distribution/list?id='+distributionData.id+'&name='+distributionData.memberName)"
@click="navigateTo(`/pages/mine/distribution/list?id=${distributionData.id}&name=${distributionData.memberName}`)"
color="#ff6b35"
name="bag-fill"
></u-icon>
@@ -20,72 +17,54 @@
</view>
<view
class="nav-item"
@click="handleClick(`/pages/mine/distribution/history?type=0&id=${distributionData.id}&name=${distributionData.memberName}`)"
@click="navigateTo(`/pages/mine/distribution/history?type=0&id=${distributionData.id}&name=${distributionData.memberName}`)"
>
<u-icon size="50" color="#ff6b35" name="order"></u-icon>
<view>分销业绩</view>
</view>
<view
class="nav-item"
@click="handleClick('/pages/mine/distribution/history?type=1')"
>
<view class="nav-item" @click="navigateTo('/pages/mine/distribution/history?type=1')">
<u-icon size="50" color="#ff6b35" name="red-packet-fill"></u-icon>
<view>提现记录</view>
</view>
<view
class="nav-item"
@click="handleClick('/pages/mine/distribution/withdrawal')"
>
<view class="nav-item" @click="navigateTo('/pages/mine/distribution/withdrawal')">
<u-icon size="50" color="#ffc71c" name="rmb-circle-fill"></u-icon>
<view>提现</view>
</view>
</view>
</view>
</template>
<script>
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { distribution } from '@/api/goods'
import { unitPrice } from '@/utils/filters.js'
import { distribution } from "@/api/goods";
export default {
const store = useStore()
const distributionData = ref<Record<string, any>>({})
data() {
return {
distributionData: "",
};
},
methods: {
handleClick(url) {
uni.navigateTo({
url,
});
},
queryGoods(src) {
uni.navigateTo({
url: `/pages/mine/distribution/${src}`,
});
},
/**
* 初始化推广商品
*/
init() {
uni.showLoading({
title: "加载中",
});
distribution().then((res) => {
if (res.data.result) {
this.distributionData = res.data.result;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
onShow() {
this.init();
},
};
onShow(() => {
fetchDistributionInfo()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function navigateTo(url: string) {
uni.navigateTo({ url })
}
function fetchDistributionInfo() {
uni.showLoading({ title: '加载中' })
distribution().then((res) => {
if (res.data.result) {
distributionData.value = res.data.result
}
hideLoadingIfNeeded()
})
}
</script>
<style lang="scss" scoped>
@@ -124,6 +103,5 @@ export default {
justify-content: center;
gap: 20rpx;
width: 33%;
// color: #fff;
}
</style>

View File

@@ -1,104 +1,127 @@
<template>
<view class="wrapper">
<view class="wrapper" :style="themeStyle">
<u-tabs
:list="list"
:list="stepList"
:scrollable="false"
v-model:current="current"
v-model:current="currentStep"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
></u-tabs>
<!-- 推广人资料 -->
<view class="message">
<u-form :model="ruleForm" label-width="250rpx" ref="uForm">
<u-form-item label="会员昵称" prop="name">
<u-input v-model="ruleForm.name" />
</u-form-item>
<u-form-item label="账户类型" prop="name"> </u-form-item>
<u-form-item
label="收款人姓名"
placeholder="请输入收款人姓名"
prop="name"
>
<u-input v-model="ruleForm.name" />
</u-form-item>
<u-form-item
label="收款账号"
placeholder="请输入收款人账号"
prop="name"
>
<u-input v-model="ruleForm.name" />
</u-form-item>
<u-form-item
label="银行名称"
placeholder="请输入开户银行支行名称"
prop="name"
>
<u-input v-model="ruleForm.name" />
</u-form-item>
</u-form>
<u-button :customStyle="{'background':$lightColor,'color':'#fff' }" @click="submit">提交</u-button>
<view class="feedBack-box">
<up-form
:model="formData"
label-position="top"
ref="uFormRef"
>
<up-form-item label="会员昵称" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
:custom-style="fieldInputStyle"
/>
</up-form-item>
<up-form-item label="账户类型" prop="name"></up-form-item>
<up-form-item label="收款人姓名" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
placeholder="请输入收款人姓名"
:custom-style="fieldInputStyle"
/>
</up-form-item>
<up-form-item label="收款账号" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
placeholder="请输入收款人账号"
:custom-style="fieldInputStyle"
/>
</up-form-item>
<up-form-item label="银行名称" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
placeholder="请输入开户银行支行名称"
:custom-style="fieldInputStyle"
/>
</up-form-item>
</up-form>
</view>
<view class="submit" @click="submitForm">提交</view>
</view>
</template>
<script>
export default {
components: {},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules);
},
data() {
return {
current:0,
lightColor: this.$lightColor,
list: [
{
name: "推广人资料",
},
{
name: "平台审核",
},
{
name: "完成",
},
],
ruleForm: {
name: "",
radio: "",
},
rules: {
name: [
{
required: true,
message: "请输入姓名",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
],
},
};
},
};
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getThemeStyle } from '@/utils/theme'
import { fieldInputStyle } from '@/utils/form-style.js'
const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const lightColor = computed(() => store.getters.lightColor)
const currentStep = ref(0)
const uFormRef = ref<any>(null)
const stepList = [
{ name: '推广人资料' },
{ name: '平台审核' },
{ name: '完成' },
]
const formData = reactive({
name: '',
radio: '',
})
const rules = {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
}
onReady(() => {
uFormRef.value?.setRules(rules)
})
function submitForm() {
uFormRef.value?.validate().catch(() => {
uni.showToast({ title: '请填写有效信息', icon: 'none' })
})
}
</script>
<style lang="scss" scoped>
.menu {
height: 88rpx;
line-height: 88rpx;
background: $main-color;
display: flex;
> .menu-item {
flex: 1;
text-align: center;
color: $light-color;
}
}
.active {
color: #fff !important;
}
.message {
padding: 0 32rpx;
<style lang="scss">
page {
background: #f8f8f8;
}
</style>
<style lang="scss" scoped>
@import '@/pages/passport/entry/seller/entry-form.scss';
.wrapper {
box-sizing: border-box;
min-height: 100vh;
padding: 20rpx 24rpx 40rpx;
background: #f8f8f8;
@include seller-entry-form;
}
.feedBack-box {
background: #fff;
border-radius: 20rpx;
padding: 32rpx;
margin-top: 20rpx;
}
.submit {
@include seller-entry-submit;
}
</style>

View File

@@ -58,44 +58,44 @@
<!-- 导航栏 -->
<view class="nav">
<view class="nav-item" @click="handleMyGoods(true)" :class="{ checked: params.checked }">已选择</view>
<view class="nav-item" @click="handleMyGoods(false)" :class="{ checked: !params.checked }">未选择</view>
<view class="nav-item" @click="toggleGoodsTab(true)" :class="{ checked: queryParams.checked }">已选择</view>
<view class="nav-item" @click="toggleGoodsTab(false)" :class="{ checked: !queryParams.checked }">未选择</view>
<!-- <view class="nav-item" @click="popup = !popup">筛选</view> -->
</view>
<!-- 商品列表 -->
<view class="goods-list">
<scroll-view class="body-view" scroll-y @scrolltolower="renderDate">
<scroll-view class="body-view" scroll-y @scrolltolower="loadMore">
<block v-for="(item, index) in goodsList" :key="item.id">
<u-swipe-action v-if="params.checked" class="distribution-swipe">
<u-swipe-action v-if="queryParams.checked" class="distribution-swipe">
<u-swipe-action-item
:show="item.___selected"
@open="openAction(item)"
@open="openSwipeAction(item)"
:name="index"
:options="options"
@click="changeActionTab(item)"
:options="swipeOptions"
@click="confirmUnbindPrompt(item)"
>
<view class="goods-item">
<view class="goods-item-img" @click="handleNavgationGoods(item)">
<view class="goods-item-img" @click="navigateToGoods(item)">
<u-image width="176rpx" height="176rpx" :src="item.thumbnail"></u-image>
</view>
<view class="goods-item-desc">
<view class="-item-title" @click="handleNavgationGoods(item)">
<view class="-item-title" @click="navigateToGoods(item)">
{{ item.goodsName }}
</view>
<view class="-item-price" @click="handleNavgationGoods(item)">
<view class="-item-price" @click="navigateToGoods(item)">
佣金:
<span> {{ unitPrice(item.commission) }}</span>
</view>
<view class="-item-bottom">
<view class="-item-bootom-money" @click="handleNavgationGoods(item)">
<view class="-item-bootom-money" @click="navigateToGoods(item)">
<view class="-item-yj">
<span>{{ unitPrice(item.price) }}</span>
</view>
</view>
<view>
<view class="click" @click="handleLink(item)">分销商品</view>
<view class="click" @click="shareDistributionGoods(item)">分销商品</view>
</view>
</view>
</view>
@@ -104,25 +104,25 @@
</u-swipe-action>
<view v-else class="goods-item">
<view class="goods-item-img" @click="handleNavgationGoods(item)">
<view class="goods-item-img" @click="navigateToGoods(item)">
<u-image width="176rpx" height="176rpx" :src="item.thumbnail"></u-image>
</view>
<view class="goods-item-desc">
<view class="-item-title" @click="handleNavgationGoods(item)">
<view class="-item-title" @click="navigateToGoods(item)">
{{ item.goodsName }}
</view>
<view class="-item-price" @click="handleNavgationGoods(item)">
<view class="-item-price" @click="navigateToGoods(item)">
佣金:
<span> {{ unitPrice(item.commission) }}</span>
</view>
<view class="-item-bottom">
<view class="-item-bootom-money" @click="handleNavgationGoods(item)">
<view class="-item-bootom-money" @click="navigateToGoods(item)">
<view class="-item-yj">
<span>{{ unitPrice(item.price) }}</span>
</view>
</view>
<view>
<view class="click" @click="handleClickGoods(item)">立即选取</view>
<view class="click" @click="selectGoods(item)">立即选取</view>
</view>
</view>
</view>
@@ -136,210 +136,164 @@
</view>
</view>
<canvas class="canvas-hide" canvas-id="qrcode" />
<drawCanvas ref="drawCanvas" v-if="showFlag" :res="res" />
<u-modal v-model:show="deleteShow" :confirm-style="{'color':lightColor}" @confirm="delectConfirm" show-cancel-button :content="deleteContent" :async-close="true"></u-modal>
<drawCanvas ref="drawCanvasRef" v-if="showPoster" :res="posterData" />
<u-modal
v-model:show="showUnbindModal"
:confirm-style="{ color: lightColor }"
@confirm="confirmUnbind"
show-cancel-button
:content="unbindModalContent"
:async-close="true"
></u-modal>
</view>
</template>
<script>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import {
distributionGoods,
checkedDistributionGoods,
getMpCode,
} from "@/api/goods";
} from '@/api/goods'
import drawCanvas from '@/components/m-canvas'
import { unitPrice } from '@/utils/filters.js'
import drawCanvas from "@/components/m-canvas";
export default {
data() {
return {
lightColor: this.$lightColor,
deleteContent: "解绑该商品?", //删除显示的信息
// 商品栏右侧滑动按钮
options: [
{
text: "解绑",
style: {
backgroundColor: this.$lightColor, //高亮颜色
},
},
],
showFlag: false, //分销分享开关
empty: false,
popup: false, //弹出层开关
active_color: this.$mainColor,
current: 0,
params: {
pageNumber: 1,
pageSize: 10,
checked: true,
},
goodsList: [],
// 分销分享 实例
res: {
container: {
width: 600,
height: 960,
background: "#fff",
title: "分享背景",
},
// 分销分享
bottom: {
img: "",
code: "",
price: 0,
},
},
routers: "",
deleteShow: false, //删除模态框
goodsVal: false, //分销商铺信息
};
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const swipeOptions = computed(() => [
{
text: '解绑',
style: { backgroundColor: lightColor.value },
},
components: {
drawCanvas,
])
const unbindModalContent = '解绑该商品?'
const showPoster = ref(false)
const showUnbindModal = ref(false)
const routeQuery = ref<Record<string, string>>({})
const selectedGoods = ref<any>(null)
const drawCanvasRef = ref<any>(null)
const queryParams = ref({
pageNumber: 1,
pageSize: 10,
checked: true,
})
const goodsList = ref<any[]>([])
const posterData = ref({
container: {
width: 600,
height: 960,
background: '#fff',
title: '分享背景',
},
onLoad(options) {
this.routers = options;
bottom: {
img: '',
code: '',
price: 0,
desc: '',
},
watch: {},
onShow() {
this.goodsList = [];
this.init();
},
methods: {
/**
* 滑动删除
*/
changeActionTab(val) {
this.deleteShow = true;
this.goodsVal = val;
},
})
/**
* 点击解绑商品
*/
delectConfirm() {
checkedDistributionGoods({ id: this.goodsVal.id, checked: false }).then(
(res) => {
if (res.data.success) {
uni.showToast({
title: "此商品解绑成功",
duration: 2000,
});
this.deleteShow = false;
this.goodsList = [];
this.init();
}
}
);
},
onLoad((options) => {
routeQuery.value = options || {}
})
/**
* 左滑打开删除
*/
openAction(val) {
this.goodsList.forEach((item) => {
item["___selected"] = false;
});
val["___selected"] = true;
},
onShow(() => {
goodsList.value = []
queryParams.value.pageNumber = 1
fetchGoodsList()
})
/**
* 查看图片
*/
handleNavgationGoods(val) {
uni.navigateTo({
url: `/pages/product/goods?id=${val.skuId}&goodsId=${val.goodsId}`,
});
},
function confirmUnbindPrompt(item: any) {
showUnbindModal.value = true
selectedGoods.value = item
}
async handleLink(goods) {
uni.showToast({
title: "请请按住保存图片",
duration: 2000,
icon: "none",
});
let page = `pages/product/goods`;
let scene = `${goods.skuId},${goods.goodsId},${this.routers.id}`;
let result = await getMpCode({ page, scene });
if (result.data.success) {
let callback = result.data.result;
this.res.container.title = `${goods.goodsName}`;
this.res.bottom.code = `data:image/png;base64,${callback}`;
this.res.bottom.price = this.unitPrice(
goods.price,
"¥"
);
this.res.bottom.desc = `${goods.goodsName}`;
this.res.bottom.img = `${goods.thumbnail}`;
function confirmUnbind() {
checkedDistributionGoods({ id: selectedGoods.value.id, checked: false }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '此商品解绑成功', duration: 2000 })
showUnbindModal.value = false
goodsList.value = []
queryParams.value.pageNumber = 1
fetchGoodsList()
}
})
}
if (this.showFlag) {
this.$refs.drawCanvas.init();
}
this.showFlag = true;
} else {
uni.showToast({
title: `制作二维码失败!请稍后重试`,
duration: 2000,
icon: "none",
});
}
},
function openSwipeAction(item: any) {
goodsList.value.forEach((row) => {
row.___selected = false
})
item.___selected = true
}
change(index) {
this.current = index;
},
// 点击我的选品库
handleMyGoods(flag) {
this.goodsList = [];
this.params.checked = flag;
this.init();
},
function navigateToGoods(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.skuId}&goodsId=${item.goodsId}`,
})
}
// 选择商品
handleClickGoods(val) {
checkedDistributionGoods({ id: val.id, checked: true }).then((res) => {
if (res.data.success) {
uni.showToast({
title: "已添加到我的选品库",
duration: 2000,
icon: "none",
});
async function shareDistributionGoods(goods: any) {
uni.showToast({ title: '请请按住保存图片', duration: 2000, icon: 'none' })
const page = 'pages/product/goods'
const scene = `${goods.skuId},${goods.goodsId},${routeQuery.value.id}`
const result = await getMpCode({ page, scene })
if (result.data.success) {
const callback = result.data.result
posterData.value.container.title = `${goods.goodsName}`
posterData.value.bottom.code = `data:image/png;base64,${callback}`
posterData.value.bottom.price = unitPrice(goods.price, '¥')
posterData.value.bottom.desc = `${goods.goodsName}`
posterData.value.bottom.img = `${goods.thumbnail}`
if (showPoster.value) {
drawCanvasRef.value?.init()
}
showPoster.value = true
} else {
uni.showToast({ title: '制作二维码失败!请稍后重试', duration: 2000, icon: 'none' })
}
}
setTimeout(() => {
this.goodsList = [];
this.init();
}, 500);
}
});
},
function toggleGoodsTab(isSelected: boolean) {
goodsList.value = []
queryParams.value.checked = isSelected
queryParams.value.pageNumber = 1
fetchGoodsList()
}
init() {
distributionGoods(this.params).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
res.data.result.records.forEach((item) => {
item["___selected"] = false;
});
this.goodsList.push(...res.data.result.records);
}
if (this.goodsList.length === 0) {
this.empty = true;
}
});
},
function selectGoods(item: any) {
checkedDistributionGoods({ id: item.id, checked: true }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '已添加到我的选品库', duration: 2000, icon: 'none' })
setTimeout(() => {
goodsList.value = []
queryParams.value.pageNumber = 1
fetchGoodsList()
}, 500)
}
})
}
/**
* 底部加载数据
*/
renderDate() {
function fetchGoodsList() {
distributionGoods(queryParams.value).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
res.data.result.records.forEach((item: any) => {
item.___selected = false
})
goodsList.value.push(...res.data.result.records)
}
})
}
this.params.pageNumber += 1;
this.init();
},
},
};
function loadMore() {
queryParams.value.pageNumber += 1
fetchGoodsList()
}
</script>
<style lang="scss" scoped>

View File

@@ -1,129 +1,182 @@
<template>
<view>
<view class="withdrawal-list">
<view class="title">提现金额</view>
<view class="content">
<view class="price">
<span> </span>
<u-input v-model="price" placeholder="" type="number" />
</view>
<view class="all">
<view @click="handleAll" :style="{ color: $mainColor }">全部</view>
<view style="font-size: 24rpx; color: #999"
>可提现金额<span>{{unitPrice(distributionData.canRebate) }}</span
></view
>
<view class="wrapper" :style="themeStyle">
<view class="feedBack-box">
<view class="box-title">提现金额</view>
<view class="amount-row">
<text class="currency"></text>
<u-input
v-model="price"
type="digit"
border="none"
placeholder="请输入提现金额"
class="amount-input"
/>
</view>
<view class="amount-extra">
<view class="all-btn" @click="fillAllAmount">全部</view>
<view class="balance-tip">
可提现金额
<text class="balance-value">{{ unitPrice(distributionData.canRebate) }}</text>
</view>
</view>
</view>
<view class="submit" @click="cashd">提现</view>
<view class="submit" @click="submitWithdraw">提现</view>
</view>
</template>
<script>
import { distribution, cash } from "@/api/goods";
export default {
data() {
return {
price: 0,
distributionData: "",
};
},
mounted() {
this.init();
},
methods: {
cashd() {
this.price = this.price + "";
if (this.$u.test.amount(parseInt(this.price))) {
cash({ price: this.price }).then((res) => {
if(res.data.success){
uni.showToast({
title: '提现成功!',
duration: 2000,
icon:"none"
});
setTimeout(()=>{
uni.navigateBack({
delta: 1
});
},1000)
}
});
} else {
uni.showToast({
title: "请输入正确金额",
duration: 2000,
icon: "none",
});
<script setup lang="ts">
import { ref, computed, getCurrentInstance, onMounted } from 'vue'
import { useStore } from '@/store'
import { distribution, cash } from '@/api/goods'
import { unitPrice } from '@/utils/filters.js'
import { getThemeStyle } from '@/utils/theme'
const store = useStore()
const { proxy } = getCurrentInstance()!
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const price = ref('')
const distributionData = ref<Record<string, any>>({})
onMounted(() => {
fetchDistributionInfo()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function fetchDistributionInfo() {
uni.showLoading({ title: '加载中' })
distribution().then((res) => {
if (res.data.result) {
distributionData.value = res.data.result
}
hideLoadingIfNeeded()
})
}
function submitWithdraw() {
const amount = String(price.value ?? '')
if (proxy.$u.test.amount(parseInt(amount))) {
cash({ price: amount }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '提现成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
},
handleAll() {
this.price = this.distributionData.canRebate;
},
/**
* 初始化推广商品
*/
init() {
uni.showLoading({
title: "加载中",
});
distribution().then((res) => {
if (res.data.result) {
this.distributionData = res.data.result;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
})
} else {
uni.showToast({ title: '请输入正确金额', duration: 2000, icon: 'none' })
}
}
function fillAllAmount() {
price.value = distributionData.value.canRebate ?? ''
}
</script>
<style lang="scss" scoped>
::v-deep .u-input__input,
.u-input {
font-size: 80rpx !important;
height: 102rpx !important;
}
::v-deep .u-input__input{
height: 100%;
font-size: 80rpx;
}
.content {
display: flex;
> .price {
width: 60%;
margin: 20rpx 0;
font-size: 80rpx;
display: flex;
}
> .all {
justify-content: center;
width: 40%;
display: flex;
flex-direction: column;
align-items: flex-end;
}
}
.withdrawal-list {
margin: 20rpx 0;
background: #fff;
padding: 16rpx 32rpx;
}
.title {
font-size: 35rpx;
}
.submit {
margin: 80rpx auto;
width: 94%;
background: $light-color;
height: 90rpx;
color: #fff;
border-radius: 10rpx;
text-align: center;
line-height: 90rpx;
<style lang="scss">
page {
background: #f8f8f8;
}
</style>
<style lang="scss" scoped>
@import '@/pages/passport/entry/seller/entry-form.scss';
.wrapper {
box-sizing: border-box;
min-height: 100vh;
padding: 20rpx 24rpx 40rpx;
background: #f8f8f8;
}
.feedBack-box {
background: #fff;
border-radius: 20rpx;
padding: 32rpx;
}
.box-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
line-height: 1.4;
}
.amount-row {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 24rpx;
padding: 16rpx 24rpx;
background: #fafafa;
border-radius: 12rpx;
}
.currency {
flex-shrink: 0;
margin-right: 16rpx;
font-size: 56rpx;
font-weight: 600;
line-height: 1;
color: #333;
}
.amount-input {
flex: 1;
min-width: 0;
}
:deep(.amount-input .u-input) {
padding: 0 !important;
}
:deep(.amount-input .u-input__content) {
background: transparent !important;
}
:deep(.amount-input .u-input__content__field-wrapper__field) {
height: 72rpx !important;
min-height: 72rpx !important;
font-size: 56rpx !important;
font-weight: 600 !important;
color: #333 !important;
}
:deep(.amount-input .u-input__content__field-wrapper__field--placeholder) {
font-size: 32rpx !important;
font-weight: 400 !important;
color: #c0c4cc !important;
}
.amount-extra {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 24rpx;
}
.all-btn {
font-size: 28rpx;
color: var(--theme-light, #ff6b35);
}
.balance-tip {
font-size: 24rpx;
color: #999;
}
.balance-value {
margin: 0 4rpx;
color: #333;
}
.submit {
@include seller-entry-submit;
}
</style>

View File

@@ -1,62 +1,42 @@
<template>
<div class="wrapper">
<u-parse :lazy-load="true" :selectable="true" :content="res.content" v-if="res"></u-parse>
<u-parse :lazy-load="true" :selectable="true" :content="article.content" v-if="article"></u-parse>
</div>
</template>
<script>
import { getArticleDetailByType } from "@/api/article";
export default {
data() {
return {
res: "",
way: {
USER_AGREEMENT: {
title: "服务协议",
type: "USER_AGREEMENT",
},
PRIVACY_POLICY: {
title: "隐私政策",
type: "PRIVACY_POLICY",
},
LICENSE_INFORMATION: {
title: "证照信息",
type: "LICENSE_INFORMATION",
},
ABOUT: {
title: "关于我们",
type: "ABOUT",
},
STORE_REGISTER: {
title: "店铺入驻协议",
type: "STORE_REGISTER",
},
},
};
},
mounted() {},
onLoad(option) {
console.log(this.way)
uni.setNavigationBarTitle({
title: this.way[option.type].title,
});
this.init(option);
},
methods: {
init(option) {
getArticleDetailByType(this.way[option.type].type).then((res) => {
if (res.data.success) {
this.res = res.data.result;
console.log(res)
}
});
},
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getArticleDetailByType } from '@/api/article'
const ARTICLE_TYPE_MAP: Record<string, { title: string; type: string }> = {
USER_AGREEMENT: { title: '服务协议', type: 'USER_AGREEMENT' },
PRIVACY_POLICY: { title: '隐私政策', type: 'PRIVACY_POLICY' },
LICENSE_INFORMATION: { title: '证照信息', type: 'LICENSE_INFORMATION' },
ABOUT: { title: '关于我们', type: 'ABOUT' },
STORE_REGISTER: { title: '店铺入驻协议', type: 'STORE_REGISTER' },
}
const article = ref<any>(null)
onLoad((option) => {
const meta = ARTICLE_TYPE_MAP[option.type]
if (!meta) return
uni.setNavigationBarTitle({ title: meta.title })
fetchArticle(meta.type)
})
function fetchArticle(type: string) {
getArticleDetailByType(type).then((res) => {
if (res.data.success) {
article.value = res.data.result
}
})
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 16rpx;
}
</style>
</style>

View File

@@ -196,515 +196,356 @@
</view>
</template>
<script>
// rpx和px的比率
var l
// 可用窗口高度
var wh
// 顶部空盒子的高度
var mgUpHeight
<script setup lang="ts">
import { ref, nextTick, getCurrentInstance } from 'vue'
import {
getTalkMessage,
onLoad,
onHide,
onUnload,
onPullDownRefresh,
onPageScroll,
} from '@dcloudio/uni-app'
import {
getTalkMessage as fetchTalkMessageApi,
getTalkByUser,
jumpObtain
} from "@/api/im.js";
import SocketService from "@/utils/socket_service.js";
import storage from "@/utils/storage.js";
import {
beautifyTime
} from "@/utils/filters.js"
jumpObtain,
} from '@/api/im.js'
import SocketService from '@/utils/socket_service.js'
import storage from '@/utils/storage.js'
import { beautifyTime, unitPrice } from '@/utils/filters.js'
import config from '@/config/config.js'
import { textReplaceEmoji, emojistwo } from '@/utils/emojis.js';
export default {
// 页面卸载后清除imGoodId
onUnload () {
// #ifdef H5
uni.setStorageSync("imGoodId", '');
// #endif
import { textReplaceEmoji, emojistwo } from '@/utils/emojis.js'
if (this.socketOpen == true) {
uni.closeSocket();
}
},
onLoad (options) {
// 没有goodsid则不显示 发送商品弹窗
this.showHideModel = options.goodsid
// 发送后刷新页面不显示 发送商品弹窗 local里面imGoodId不为空显示
// #ifdef H5
this.localImGoodsId = uni.getStorageSync("imGoodId");
// #endif
this.resolve = options
// 请求商品信息
if (this.resolve.goodsid) {
this.commodityDetails()
}
var query = uni.getSystemInfoSync()
// rpx和px的比率
let l: number
// 可用窗口高度
let wh: number
// 顶部空盒子的高度
let mgUpHeight: number
l = query.screenWidth / 750
wh = query.windowHeight
this.scrollHeight = (query.windowHeight - 44) + "px"
this.user = storage.getUserInfo()
this.toUser = storage.getTalkToUser()
const { proxy } = getCurrentInstance()!
if (options.talkId) {
this.params.talkId = options.talkId;
this.getTalkMessage()
} else {
this.getTalk(options.userId)
const socketOpen = ref(false)
const showHideModel = ref<string | undefined>(undefined)
const localImGoodsId = ref('')
const showHide = ref(true)
const anData = ref<Record<string, any>>({})
const animationData = ref<Record<string, any>>({})
const msgList = ref<any[]>([])
const oldHeight = ref(0)
const params = ref({
talkId: '',
pageSize: 10,
pageNumber: 1,
})
const msg = ref('')
const go = ref(0)
const user = ref<Record<string, any>>({})
const toUser = ref<Record<string, any>>({})
const scrollHeight = ref(0)
const ws = new SocketService()
const resolve = ref<Record<string, any>>({})
const goodListData = ref<Record<string, any>>({})
const reconnectCount = ref(0)
const inputHeight = ref(0)
const isShow = ref(false)
}
// this.ws.connect();
this.socket();
},
// 页面隐藏
onHide () {
uni.closeSocket();
},
onUnload () {
uni.closeSocket();
},
onPullDownRefresh () {
this.params.pageNumber = this.params.pageNumber + 1
this.getTalkMessage()
setTimeout(function () {
uni.stopPullDownRefresh();
}, 1000);
},
data () {
return {
textReplaceEmoji,
emojistwo,
socketOpen: false, //是否连接
storage,
fixed: 'fixed',
bottom: '50px',
width: '100%',
showHideModel: undefined,
localImGoodsId: '',
showHide: true,
msgLoad: false,
anData: {},
animationData: {},
msgList: [],
oldHeight: 0,
params: { //搜索条件
talkId: '',
pageSize: 10,
pageNumber: 1,
},
goToIndex: 0, // 前往位置
msg: "",
go: 0,
newMessageNum: 0,
user: {},
toUser: {},
scrollHeight: 0,
ws: new SocketService(),
resolve: {},
goodListData: {},
count: 0, //判断socket断开连接请求次数
inputHeight:0,
isShow:false,
}
},
onPageScroll (e) {
// #ifdef APP-PLUS
uni.hideKeyboard()
this.isShow = false
// #endif
},
methods: {
navigateToBottom(){
// #ifdef H5
this.isShow = true
this.$refs.inputRef.focus()
// #endif
// #ifdef APP-PLUS
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 5000000,
duration: 50,
success: () => {
setTimeout(() => {
this.isShow = true
}, 200);
;
},
fail: () => { },
complete: () => {}
});
});
// #endif
},
eventHandle(){
this.inputHeight = 0
this.isShow = false
},
inputBindFocus(e){
if (e.detail.height) {
// #ifdef APP-PLUS
// 判断是否是ios
if (uni.getSystemInfoSync().platform == 'ios') {
this.inputHeight = e.detail.height - 40 //这个高度就是软键盘的高度
}else{
this.inputHeight = e.detail.height
}
// #endif
// #ifndef APP-PLUS
this.inputHeight = e.detail.height //这个高度就是软键盘的高度
// #endif
}
},
sendMessage () {
if (this.msg == "") {
return 0;
}
if (this.socketOpen == false) {
return
}
let msg = {
operation_type: "MESSAGE",
to: this.toUser.userId,
from: this.user.id,
message_type: "MESSAGE",
context: this.msg,
talk_id: this.params.talkId,
}
let data = JSON.stringify(msg);
uni.sendSocketMessage({
data: data,
});
this.msgList.push({
"text": this.msg,
"my": true,
"messageType": 'MESSAGE'
})
let type = 'down';
this.msgGo(type)
this.msg = ""
},
sendGoodsMessage () {
let msg = {
operation_type: "MESSAGE",
to: this.toUser.userId,
from: this.user.id,
message_type: "GOODS",
context: this.goodListData,
talk_id: this.params.talkId,
}
let data = JSON.stringify(msg);
uni.sendSocketMessage({
data: data
});
this.msgList.push({
"text": JSON.stringify(this.goodListData),
"my": true,
"messageType": 'GOODS'
})
this.showHide = false
// #ifdef H5
uni.setStorageSync("imGoodId", 1111111);
// #endif
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 2000000,
duration: 300
});
})
},
socket () {
var _this = this;
uni.closeSocket();
this.socketOpen = false;
try {
//WebSocket的地址
var url = config.baseWsUrl + '/' + storage.getAccessToken();
// 连接
uni.connectSocket({
url: url,
});
// 监听WebSocket连接已打开
uni.onSocketOpen(function (res) {
_this.socketOpen = true;
});
if (!this.socketOpen) {
// 监听连接失败
uni.onSocketError(function (err) {
if (this.count < 3) {
if (err && err.code != 1000) {
_this.socketOpen = true;
setTimeout(() => {
uni.connectSocket({
url: url,
});
}, 2000)
}
} else {
uni.closeSocket();
}
this.count++
});
}
// 监听收到信息
uni.onSocketMessage(function (res) {
res.data = JSON.parse(res.data)
console.log(res.data.result);
if (res.data.messageResultType == 'MESSAGE') {
_this.msgList.push(res.data.result)
console.log(_this.msgList)
}
console.log(res.data)
_this.msgGo()
})
} catch (e) {
uni.closeSocket();
}
// 监听是否断线,断线进行重新连接
uni.onSocketClose((res) => {
if (res.code != null && res.code != 1000) {
this.socket()
}
})
},
beautifyTime,
//订单详情
linkTosOrders (val) {
let order = JSON.parse(val)
uni.navigateTo({
url: '/pages/order/orderDetail?sn=' + order.sn,
});
},
// 跳转商品详情页
jumpGoodDesc (item) {
let info = JSON.parse(item.text)
uni.navigateTo({
url: `/pages/product/goods?id=${info.id}&goodsId=${info.goodsId}`,
});
},
//取消发送
cancelModel () {
this.showHide = false
},
// 请求商品详情
commodityDetails () {
jumpObtain(this.resolve.skuid, this.resolve.goodsid).then((res) => {
this.goodListData = res.data.result.data
})
},
// 切换输入法时移动输入框(按照官方的上推页面的原理应该会自动适应不同的键盘高度-->官方bug)
goPag (kh) {
this.retractBox(0, 250)
if (this.keyHeight != 0) {
if (kh - this.keyHeight > 0) {
this.retractBox(this.keyHeight - kh, 250)
}
}
},
// 移动顶部的空盒子
messageBoxMove (x, t) {
var animation = uni.createAnimation({
duration: t,
timingFunction: 'linear',
})
this.animation = animation
animation.height(x).step()
this.anData = animation.export()
},
// 保持消息体可见
msgGo (type) {
const query = uni.createSelectorQuery()
// 延时100ms保证是最新的高度
setTimeout(() => {
// 获取消息体高度
query.select('#msgList').boundingClientRect(data => {
// 如果超过scorll高度就滚动scorll
if (type == 'up') {
this.go = data.height - this.oldHeight
} else if (type == 'down') {
this.go = data.height - wh + 120
}
// if (this.oldHeight > 0) {
// this.go = data.height - this.oldHeight
// } else {
// // if (data.height - (wh - 32) > 0) {
// this.go = data.height - wh + 120
// }
// 保证键盘第一次拉起时消息体能保持可见
var moveY = wh - data.height
// 超出页面则缩回空盒子
if (moveY - mgUpHeight < 0) {
// 小于0则视为0
if (moveY < 0) {
this.messageBoxMove(0, 200)
} else {
// 否则缩回盒子对应的高度
this.messageBoxMove(moveY, 200)
}
}
uni.pageScrollTo({
scrollTop: this.go,
duration: 0
})
this.oldHeight = data.height
}).exec();
}, 100)
},
// 回答问题的业务逻辑
answer (id) {
// 这里应该传入问题的id,模拟就用index代替了
},
// 不建议输入框聚焦时操作此动画
ckAdd () {
if (!this.showTow) {
this.retractBox(-180, 350)
} else {
this.retractBox(0, 200)
}
this.showTow = !this.showTow
},
hideKey () {
uni.hideKeyboard()
},
// 拉起/收回附加栏
retractBox (x, t) {
var animation = uni.createAnimation({
duration: t,
timingFunction: 'ease',
})
this.animation = animation
animation.translateY(x).step()
this.animationData = animation.export()
},
async getTalkMessage () {
let type = '';
await getTalkMessage(this.params).then(res => {
if (res.data.success) {
if (this.msgList.length >= 10) {
this.msgList.unshift(...res.data.result)
type = 'up'
} else {
this.msgList.unshift(...res.data.result)
type = 'down'
}
this.msgList.forEach(item => {
if (item.fromUser === this.user.id) {
item.my = true
}
})
}
})
console.log(this.msgList);
this.msgGo(type)
},
// 上拉加载
touchMoreMessage (e) {
if (e.target.scrollTop == 0) {
this.params.pageNumber = this.params.pageNumber + 1
this.getTalkMessage()
}
},
async getTalk (userId) {
getTalkByUser(userId).then(res => {
if (res.data.success) {
this.toUser = res.data.result
this.params.talkId = res.data.result.id
this.getTalkMessage()
}
})
},
// 处理消息时间是否显示
compareTime (index, datetime) {
if (datetime == undefined) {
return false;
}
if (typeof datetime == "number") {
datetime = this.unixToDate(datetime, "yyyy-MM-dd hh:mm");
}
if (this.msgList[index].is_revoke == 1) {
return false;
}
if (datetime) {
datetime = datetime.replace(/-/g, "/");
}
let time = Math.floor(Date.parse(datetime) / 1000);
let currTime = Math.floor(new Date().getTime() / 1000);
// 当前时间5分钟内时间不显示
if (currTime - time < 300) return false;
// 判断是否是最后一条消息,最后一条消息默认显示时间
if (index == this.msgList.length - 1) {
return true;
}
let nextDate
if (this.msgList[index + 1] && this.msgList[index + 1].createTime) {
nextDate = this.msgList[index + 1].createTime.replace(/-/g, "/");
if (nextDate - datetime < 300) return false;
}
return !(
this.unixToDate(new Date(datetime), "{y}-{m}-{d} {h}:{i}") ==
this.unixToDate(new Date(nextDate), "{y}-{m}-{d} {h}:{i}")
);
},
/**
* 将unix时间戳转换为指定格式
* @param unix 时间戳【秒】
* @param format 转换格式
* @returns {*|string}
*/
unixToDate (unix, format) {
if (!unix) return unix;
let _format = format || "yyyy-MM-dd hh:mm:ss";
const d = new Date(unix);
const o = {
"M+": d.getMonth() + 1,
"d+": d.getDate(),
"h+": d.getHours(),
"m+": d.getMinutes(),
"s+": d.getSeconds(),
"q+": Math.floor((d.getMonth() + 3) / 3),
S: d.getMilliseconds(),
};
if (/(y+)/.test(_format))
_format = _format.replace(
RegExp.$1,
(d.getFullYear() + "").substr(4 - RegExp.$1.length)
);
for (const k in o)
if (new RegExp("(" + k + ")").test(_format))
_format = _format.replace(
RegExp.$1,
RegExp.$1.length === 1 ?
o[k] :
("00" + o[k]).substr(("" + o[k]).length)
);
return _format;
},
onLoad((options: Record<string, string | undefined> = {}) => {
showHideModel.value = options.goodsid
// #ifdef H5
localImGoodsId.value = uni.getStorageSync('imGoodId')
// #endif
resolve.value = options
if (resolve.value.goodsid) {
commodityDetails()
}
const query = uni.getSystemInfoSync()
l = query.screenWidth / 750
wh = query.windowHeight
scrollHeight.value = query.windowHeight - 44 + 'px'
user.value = storage.getUserInfo()
toUser.value = storage.getTalkToUser()
if (options.talkId) {
params.value.talkId = options.talkId
fetchTalkMessages()
} else {
getTalk(options.userId!)
}
socket()
})
onHide(() => {
uni.closeSocket()
})
onUnload(() => {
// #ifdef H5
uni.setStorageSync('imGoodId', '')
// #endif
uni.closeSocket()
})
onPullDownRefresh(() => {
params.value.pageNumber = params.value.pageNumber + 1
fetchTalkMessages()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
})
onPageScroll(() => {
// #ifdef APP-PLUS
uni.hideKeyboard()
isShow.value = false
// #endif
})
function navigateToBottom() {
// #ifdef H5
isShow.value = true
;(proxy as any).$refs.inputRef?.focus()
// #endif
// #ifdef APP-PLUS
nextTick(() => {
uni.pageScrollTo({
scrollTop: 5000000,
duration: 50,
success: () => {
setTimeout(() => {
isShow.value = true
}, 200)
},
fail: () => {},
complete: () => {},
})
})
// #endif
}
function eventHandle() {
inputHeight.value = 0
isShow.value = false
}
function inputBindFocus(e: any) {
if (e.detail.height) {
// #ifdef APP-PLUS
if (uni.getSystemInfoSync().platform == 'ios') {
inputHeight.value = e.detail.height - 40
} else {
inputHeight.value = e.detail.height
}
// #endif
// #ifndef APP-PLUS
inputHeight.value = e.detail.height
// #endif
}
}
function sendMessage() {
if (msg.value == '') {
return 0
}
if (socketOpen.value == false) {
return
}
const payload = {
operation_type: 'MESSAGE',
to: toUser.value.userId,
from: user.value.id,
message_type: 'MESSAGE',
context: msg.value,
talk_id: params.value.talkId,
}
const data = JSON.stringify(payload)
uni.sendSocketMessage({
data: data,
})
msgList.value.push({
text: msg.value,
my: true,
messageType: 'MESSAGE',
})
const type = 'down'
msgGo(type)
msg.value = ''
}
function sendGoodsMessage() {
const payload = {
operation_type: 'MESSAGE',
to: toUser.value.userId,
from: user.value.id,
message_type: 'GOODS',
context: goodListData.value,
talk_id: params.value.talkId,
}
const data = JSON.stringify(payload)
uni.sendSocketMessage({
data: data,
})
msgList.value.push({
text: JSON.stringify(goodListData.value),
my: true,
messageType: 'GOODS',
})
showHide.value = false
// #ifdef H5
uni.setStorageSync('imGoodId', 1111111)
// #endif
nextTick(() => {
uni.pageScrollTo({
scrollTop: 2000000,
duration: 300,
})
})
}
function socket() {
uni.closeSocket()
socketOpen.value = false
try {
const url = config.baseWsUrl + '/' + storage.getAccessToken()
uni.connectSocket({
url: url,
})
uni.onSocketOpen(function () {
socketOpen.value = true
})
if (!socketOpen.value) {
uni.onSocketError(function (err: any) {
if (reconnectCount.value < 3) {
if (err && err.code != 1000) {
socketOpen.value = true
setTimeout(() => {
uni.connectSocket({
url: url,
})
}, 2000)
}
} else {
uni.closeSocket()
}
reconnectCount.value++
})
}
uni.onSocketMessage(function (res) {
const data = JSON.parse(res.data as string)
console.log(data.result)
if (data.messageResultType == 'MESSAGE') {
msgList.value.push(data.result)
console.log(msgList.value)
}
console.log(data)
msgGo()
})
} catch (e) {
uni.closeSocket()
}
uni.onSocketClose((res) => {
if (res.code != null && res.code != 1000) {
socket()
}
})
}
function linkTosOrders(val: string) {
const order = JSON.parse(val)
uni.navigateTo({
url: '/pages/order/orderDetail?sn=' + order.sn,
})
}
function jumpGoodDesc(item: any) {
const info = JSON.parse(item.text)
uni.navigateTo({
url: `/pages/product/goods?id=${info.id}&goodsId=${info.goodsId}`,
})
}
function cancelModel() {
showHide.value = false
}
function commodityDetails() {
jumpObtain(resolve.value.skuid, resolve.value.goodsid).then((res) => {
goodListData.value = res.data.result.data
})
}
function messageBoxMove(x: number, t: number) {
const animation = uni.createAnimation({
duration: t,
timingFunction: 'linear',
})
animation.height(x).step()
anData.value = animation.export()
}
function msgGo(type?: string) {
const query = uni.createSelectorQuery()
setTimeout(() => {
query
.select('#msgList')
.boundingClientRect((data: any) => {
if (type == 'up') {
go.value = data.height - oldHeight.value
} else if (type == 'down') {
go.value = data.height - wh + 120
}
const moveY = wh - data.height
if (moveY - mgUpHeight < 0) {
if (moveY < 0) {
messageBoxMove(0, 200)
} else {
messageBoxMove(moveY, 200)
}
}
uni.pageScrollTo({
scrollTop: go.value,
duration: 0,
})
oldHeight.value = data.height
})
.exec()
}, 100)
}
async function fetchTalkMessages() {
let type = ''
await fetchTalkMessageApi(params.value).then((res) => {
if (res.data.success) {
if (msgList.value.length >= 10) {
msgList.value.unshift(...res.data.result)
type = 'up'
} else {
msgList.value.unshift(...res.data.result)
type = 'down'
}
msgList.value.forEach((item) => {
if (item.fromUser === user.value.id) {
item.my = true
}
})
}
})
console.log(msgList.value)
msgGo(type)
}
function getTalk(userId: string) {
getTalkByUser(userId).then((res) => {
if (res.data.success) {
toUser.value = res.data.result
params.value.talkId = res.data.result.id
fetchTalkMessages()
}
})
}
</script>

View File

@@ -9,12 +9,12 @@
:border="false"
:auto-back="true"
></u-navbar>
<scroll-view class="list-scroll-content" scroll-y @scrolltolower="loadData(tabIndex)">
<scroll-view class="list-scroll-content" scroll-y @scrolltolower="fetchTalkList">
<!-- 消息列表 -->
<div class="iconBox">
<view class="icon-list">
<view class="icon-item" @click="cleanUnread()">
<view class="icon-item" @click="clearUnreadMessages()">
<div class="bag bag1">
<u-icon name="trash" size="50" color="#fff"></u-icon>
</div>
@@ -28,7 +28,7 @@
</view>
</view>
</div>
<u-search class="nav-search" v-model="userName" clearabled @change="userTalkList()" placeholder="搜索用户"
<u-search class="nav-search" v-model="userName" clearabled @change="fetchTalkList()" placeholder="搜索用户"
:show-action="false"></u-search>
<view class="talk-view" :key="index" v-for="(item, index) in talkList">
<view>
@@ -65,85 +65,80 @@
</view>
</template>
<script>
import { getTalkList, clearmeaager } from "@/api/im.js";
import storage from "@/utils/storage.js";
import { beautifyTime } from "@/utils/filters.js"
export default {
data () {
return {
storage,
count: {
loadStatus: "more",
},
talkList: [], //聊天列表
userName: '',
pointData: {}, //累计获取 未输入 集合
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getTalkList, clearmeaager } from '@/api/im.js'
import storage from '@/utils/storage.js'
import { beautifyTime } from '@/utils/filters.js'
onShow () {
this.userTalkList();
},
onPullDownRefresh () {
this.userTalkList()
console.log('下拉事件');
setTimeout(function () {
uni.stopPullDownRefresh();
}, 1000);
},
/**
* 触底加载
*/
onReachBottom () {
this.userTalkList();
},
methods: {
beautifyTime,
onclickToTalkInfo (val) {
storage.setTalkToUser(val)
uni.navigateTo({
url:
"/pages/mine/im/index?talkId=" + val.id,
});
},
/**
* 获取聊天列表
*/
userTalkList () {
let params = {
userName: this.userName,
}
uni.showLoading({
title: "加载中",
});
getTalkList(params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (res.data.success) {
this.talkList = res.data.result;
console.log(this.talkList, 'this.talkListthis.talkList');
}
});
},
navigateTo (url) {
uni.navigateTo({
url,
});
},
cleanUnread () {
clearmeaager().then((res) => {
console.log(res);
if (res.data.code == 200) {
this.userTalkList();
uni.showToast({
icon: "none",
title: res.data.message,
});
}
const store = useStore()
const talkList = ref<any[]>([])
const userName = ref('')
onShow(() => {
fetchTalkList()
})
onPullDownRefresh(() => {
fetchTalkList()
console.log('下拉事件')
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
})
onReachBottom(() => {
fetchTalkList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function onclickToTalkInfo(val: any) {
storage.setTalkToUser(val)
uni.navigateTo({
url: '/pages/mine/im/index?talkId=' + val.id,
})
}
function fetchTalkList() {
const params = {
userName: userName.value,
}
uni.showLoading({
title: '加载中',
})
getTalkList(params).then((res) => {
hideLoadingIfNeeded()
if (res.data.success) {
talkList.value = res.data.result
console.log(talkList.value, 'this.talkListthis.talkList')
}
})
}
function navigateTo(url: string) {
uni.navigateTo({
url,
})
}
function clearUnreadMessages() {
clearmeaager().then((res) => {
console.log(res)
if (res.data.code == 200) {
fetchTalkList()
uni.showToast({
icon: 'none',
title: res.data.message,
})
},
},
};
}
})
}
</script>
<style lang="scss" scoped>

View File

@@ -1,183 +1,98 @@
<template>
<view class="container">
<view class="list-cell b-b m-t" hover-class="cell-hover" :hover-stay-time="50">
<u-row gutter="12" justify="start" @click="navigateTo('/pages/msgTips/sysMsg/index')">
<u-col span="2" class="uCol" style="text-align:center;">
<image class="img" src="/static/mine/setting.png"></image>
</u-col>
<u-col span="7">
<p class="tit_title">系统消息</p>
<p class="tit_tips">查看系统消息</p>
</u-col>
<u-col span="3">
<view class="cell-more">
<u-tag size="mini" v-if="no_read.system_num>0" shape="circle" mode="dark" type="error" :text="no_read.system_num"></u-tag>
<span class="yticon icon-you"></span>
</view>
</u-col>
</u-row>
</view>
<!-- <view class="list-cell b-b m-t" hover-class="cell-hover" :hover-stay-time="50">
<u-row gutter="12" justify="start" @click="navigateTo('/pages/msgTips/packagemsg/index')">
<u-col span="2" class="uCol" style="text-align:center;">
<image class="img" src="/static/mine/logistics.png"></image>
</u-col>
<u-col span="7">
<p class="tit_title">物流消息</p>
<p class="tit_tips">查看物流消息</p>
</u-col>
<u-col span="3">
<view class="cell-more">
<u-tag v-if="no_read.logistics_num>0" shape="circle" mode="dark" type="warning" :text="no_read.logistics_num"></u-tag>
<span class="yticon icon-you"></span>
</view>
</u-col>
</u-row>
</view> -->
</view>
<view class="container">
<view class="list-cell b-b m-t" hover-class="cell-hover" :hover-stay-time="50">
<u-row gutter="12" justify="start" @click="navigateTo('/pages/mine/msgTips/sysMsg/index')">
<u-col span="2" class="uCol" style="text-align: center">
<image class="img" src="/static/mine/setting.png"></image>
</u-col>
<u-col span="7">
<p class="tit_title">系统消息</p>
<p class="tit_tips">查看系统消息</p>
</u-col>
<u-col span="3">
<view class="cell-more">
<u-tag
size="mini"
v-if="unreadCount.system_num > 0"
shape="circle"
mode="dark"
type="error"
:text="String(unreadCount.system_num)"
></u-tag>
<span class="yticon icon-you"></span>
</view>
</u-col>
</u-row>
</view>
</view>
</template>
<script>
import {
mapMutations
} from "vuex";
import * as API_Message from "@/api/members.js";
export default {
data() {
return {
no_read: ''
};
},
onLoad() {
this.GET_NoReadMessageNum();
},
methods: {
...mapMutations(["logout"]),
navigateTo(url) {
uni.navigateTo({
url
});
},
/** 获取未读消息数量信息 */
GET_NoReadMessageNum() {
API_Message.getNoReadMessageNum().then(response => {
this.no_read = response.data
})
}
}
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getNoReadMessageNum } from '@/api/members.js'
const unreadCount = ref<Record<string, number>>({ system_num: 0 })
onLoad(() => {
fetchUnreadCount()
})
function navigateTo(url: string) {
uni.navigateTo({ url })
}
function fetchUnreadCount() {
getNoReadMessageNum().then((response) => {
unreadCount.value = response.data || { system_num: 0 }
})
}
</script>
<style scoped lang='scss'>
.uCol {
display: flex;
justify-content: center !important;
}
.img {
width: 60rpx;
height: 60rpx;
}
.container {
background: #f9f9f9;
}
::v-deep .u-col-2 {
height: 60px;
line-height: 60px;
text-align: center !important;
}
.qicon {
text-align: center;
display: block;
font-size: 20px;
}
.redBox {
display: inline-block;
text-align: center;
line-height: 1.5em;
font-size: 12px;
min-width: 1.5em;
min-height: 1.5em;
background: #ed6533;
border-radius: 50%;
color: #fff;
}
.tit_title {
color: $u-main-color;
}
.tit_tips {
color: $u-tips-color;
}
.u-col-3 {
text-align: right !important;
padding-right: 20rpx !important;
}
.list-cell {
background: #fff;
align-items: baseline;
padding: 20rpx 0;
line-height: 60rpx;
background: #fff;
justify-content: center;
&.log-out-btn {
margin-top: 40rpx;
.cell-tit {
color: $uni-color-primary;
text-align: center;
margin-right: 0;
}
}
&.cell-hover {
background: #fafafa;
}
&.b-b:after {
left: 30rpx;
}
&.m-t {
margin-top: 16rpx;
}
.cell-more {
/* margin-top: 10rpx; */
height: 60rpx;
text-align: right;
/* display: flex;
justify-content: center; //这个是X轴居中
align-items: center; //这个是 Y轴居中 */
font-size: $font-lg;
color: $font-color-light;
/* width: 100rpx; */
}
.cell-tit {
flex: 1;
font-size: $font-base + 2rpx;
color: $font-color-dark;
margin-right: 10rpx;
}
.cell-tip {
font-size: $font-base;
color: $font-color-light;
}
}
<style scoped lang="scss">
.uCol {
display: flex;
justify-content: center !important;
}
.img {
width: 60rpx;
height: 60rpx;
}
.container {
background: #f9f9f9;
}
::v-deep .u-col-2 {
height: 60px;
line-height: 60px;
text-align: center !important;
}
.tit_title {
color: $u-main-color;
}
.tit_tips {
color: $u-tips-color;
}
.u-col-3 {
text-align: right !important;
padding-right: 20rpx !important;
}
.list-cell {
background: #fff;
align-items: baseline;
padding: 20rpx 0;
line-height: 60rpx;
justify-content: center;
&.cell-hover {
background: #fafafa;
}
&.m-t {
margin-top: 16rpx;
}
.cell-more {
height: 60rpx;
text-align: right;
font-size: $font-lg;
color: $font-color-light;
}
}
</style>

View File

@@ -1,99 +1,113 @@
<template>
<view class="container " style="font-size: 13px;">
<block v-for="(row, index) in messageList" :key="index">
<view class="msgItem">
<div class="msgMsg">
<div class="bagbar">{{$u.timeFormat(row.send_time, 'yyyy-mm-dd')}}</div>
</div>
<u-card @click="goDetail(row.sn,row.logi_id,row.ship_no)" :title="title" title-color="#666666" title-size="24" sub-title-color="#666666" sub-title-size="24" :border="false" :sub-title=row.status>
<template #body>
<view class="msg-body">
<image class="msgImg" :src="row.goods_img" mode=""></image>
<view class="msgView">
<view>{{row.goodsName}}</view>
<view class="msgNum">订单号:{{row.sn}}</view>
</view>
</view>
</template>
</u-card>
</view>
</block>
<uni-load-more :status="loadStatus"></uni-load-more>
<view class="container" style="font-size: 13px">
<block v-for="(row, index) in messageList" :key="index">
<view class="msgItem">
<div class="msgMsg">
<div class="bagbar">{{ formatSendTime(row.send_time) }}</div>
</div>
<u-card
@click="navigateToLogisticsDetail(row.sn, row.logi_id, row.ship_no)"
:title="pageTitle"
title-color="#666666"
title-size="24"
sub-title-color="#666666"
sub-title-size="24"
:border="false"
:sub-title="row.status"
>
<template #body>
<view class="msg-body">
<image class="msgImg" :src="row.goods_img" mode=""></image>
<view class="msgView">
<view>{{ row.goodsName }}</view>
<view class="msgNum">订单号:{{ row.sn }}</view>
</view>
</view>
</template>
</u-card>
</view>
</block>
<uni-load-more :status="loadStatus"></uni-load-more>
</view>
</template>
<script>
import * as API_Message from "@/api/message.js";
export default {
data() {
return {
messageList: [],
title: "物流更新通知",
subTitle: "运输中",
loadStatus:'more',
params: {
pageNumber: 1,
pageSize: 10,
},
loadStatus:'more'
};
},
onLoad(){
this.GET_LogisticsList(true);
},
onReachBottom() {
this.params.pageNumber++
this.GET_LogisticsList(false)
},
methods: {
goDetail(sn,logi_id,ship_no){
uni.navigateTo({
url:'/pages/msgTips/packagemsg/logisticsDetail?order_sn=' + sn +'&logi_id='+logi_id+'&ship_no='+ship_no,
})
},
//获取物流消息
GET_LogisticsList(reset){
if (reset) {
this.params.pageNumber = 1
}
uni.showLoading({
title:"加载中"
})
API_Message.getLogisticsMessages(this.params).then(async response => {
if (this.$store.state.isShowToast){ uni.hideLoading() }
const { data } = response
if (!data || !data.length) {
this.messageList.push(...data.data)
}
})
}
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Message from '@/api/message.js'
const store = useStore()
const { proxy } = getCurrentInstance()!
const messageList = ref<any[]>([])
const pageTitle = '物流更新通知'
const loadStatus = ref('more')
const queryParams = ref({ pageNumber: 1, pageSize: 10 })
onLoad(() => {
fetchLogisticsList(true)
})
onReachBottom(() => {
queryParams.value.pageNumber++
fetchLogisticsList(false)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function formatSendTime(time: number) {
return proxy.$u.timeFormat(time, 'yyyy-mm-dd')
}
function extractRecords(res: any) {
if (Array.isArray(res?.result?.records)) return res.result.records
if (Array.isArray(res?.result)) return res.result
if (Array.isArray(res?.data)) return res.data
return []
}
function navigateToLogisticsDetail(sn: string, logiId: string, shipNo: string) {
uni.navigateTo({
url: `/pages/mine/msgTips/packageMsg/logisticsDetail?order_sn=${sn}&logi_id=${logiId}&ship_no=${shipNo}`,
})
}
function fetchLogisticsList(reset: boolean) {
if (reset) {
queryParams.value.pageNumber = 1
messageList.value = []
loadStatus.value = 'more'
}
};
uni.showLoading({ title: '加载中' })
API_Message.getLogisticsMessages(queryParams.value).then((response) => {
hideLoadingIfNeeded()
const records = extractRecords(response.data)
if (records.length) {
messageList.value.push(...records)
} else {
loadStatus.value = 'noMore'
}
})
}
</script>
<style scoped lang='scss'>
.ddnumber {
color: $u-tips-color;
font-size: 24rpx;
}
.msg-body{
display: flex;
background-color: rgba(102, 110, 232, 0.0470588235294118);
.msgImg{
width: 160rpx;
height: 160rpx;
}
.msgView{
margin-left: 20rpx;
.msgNum:last-child{
margin-top: 60rpx;
}
}
<style scoped lang="scss">
.msg-body {
display: flex;
background-color: rgba(102, 110, 232, 0.0470588235294118);
.msgImg {
width: 160rpx;
height: 160rpx;
}
.msgView {
margin-left: 20rpx;
.msgNum:last-child {
margin-top: 60rpx;
}
}
}
.bagbar {
display: inline;
@@ -103,13 +117,8 @@ export default {
padding: 10rpx 20rpx;
background: $u-info-disabled;
}
.storeImg {
width: 100%;
height: 100rpx;
margin-right: 20rpx;
}
.container {
background: #F9F9F9;
background: #f9f9f9;
min-height: 100vh;
}
.msgMsg {
@@ -119,12 +128,8 @@ export default {
.msgItem {
padding: 1em 0;
}
view{
font-size: 13px;
color: #666666;
view {
font-size: 13px;
color: #666666;
}
u-card{
font-size: 13px;
color: #666666;
}
</style>
</style>

View File

@@ -2,21 +2,23 @@
<view class="logistics-detail">
<view class="card">
<view class="card-title">
<span>{{ logiList.shipper }}</span>快递 <span>{{ logiList.logisticCode }}</span>
<span>{{ logisticsInfo.shipper }}</span>快递 <span>{{ logisticsInfo.logisticCode }}</span>
</view>
<view class="time-line">
<u-time-line v-if="logiList.traces && logiList.traces.length != 0">
<u-time-line-item nodeTop="2" v-for="(item, index) in logiList.traces" :key="index">
<!-- 此处自定义了左边内容用一个图标替代 -->
<template v-slot:node >
<view v-if="index == logiList.traces.length - 1" class="u-node" :style="{ background: $lightColor }" style="padding: 0 4px">
<!-- 此处为uView的icon组件 -->
<u-time-line v-if="logisticsInfo.traces && logisticsInfo.traces.length != 0">
<u-time-line-item nodeTop="2" v-for="(item, index) in logisticsInfo.traces" :key="index">
<template #node>
<view
v-if="index == logisticsInfo.traces.length - 1"
class="u-node"
:style="{ background: lightColor }"
style="padding: 0 4px"
>
<u-icon name="pushpin-fill" color="#fff" :size="24"></u-icon>
</view>
</template>
<template v-slot:content>
<template #content>
<view>
<!-- <view class="u-order-title">待取件</view> -->
<view class="u-order-desc">{{ item.AcceptStation }}</view>
<view class="u-order-time">{{ item.AcceptTime }}</view>
</view>
@@ -29,32 +31,26 @@
</view>
</template>
<script>
import { getExpress } from "@/api/trade.js";
export default {
data() {
return {
express: "",
resData: {
title: "物流详情",
},
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getExpress } from '@/api/trade.js'
logiList: "",
activeStep: 0,
};
},
methods: {
init(sn) {
getExpress(sn).then((res) => {
this.logiList = res.data.result;
});
},
},
onLoad(option) {
let sn = option.order_sn;
this.init(sn);
},
};
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const logisticsInfo = ref<Record<string, any>>({})
onLoad((option) => {
fetchLogistics(option.order_sn)
})
function fetchLogistics(orderSn: string) {
getExpress(orderSn).then((res) => {
logisticsInfo.value = res.data.result || {}
})
}
</script>
<style lang="scss">
@@ -79,9 +75,6 @@ export default {
padding: 16rpx 32rpx;
}
}
.u-order-title {
font-weight: bold;
}
.u-order-desc {
font-size: 26rpx;
color: #666;

View File

@@ -30,20 +30,11 @@
</view>
</template>
<script>
import { mapMutations } from "vuex";
export default {
data() {
return {};
},
methods: {
...mapMutations(["logout"]),
}
};
<script setup lang="ts">
// 占位页,后续接入客服消息
</script>
<style scoped lang='scss'>
<style scoped lang="scss">
.msgTime {
font-size: 13px;
}
@@ -56,11 +47,6 @@ page {
vertical-align: middle;
border-radius: 0.4em;
}
.qicon {
text-align: center;
display: block;
font-size: 20px;
}
.redBox {
padding: 10rpx 12rpx;
display: inline-block;
@@ -71,7 +57,6 @@ page {
height: 1em;
background: #ed6533;
border-radius: 50%;
color: #fff;
}
.tit_title,
@@ -92,25 +77,14 @@ page {
}
.list-cell {
align-items: baseline;
padding: 20rpx 30rpx;
padding: 20rpx 30rpx;
line-height: 60rpx;
position: relative;
background: #fff;
justify-content: center;
&.log-out-btn {
margin-top: 40rpx;
.cell-tit {
color: $uni-color-primary;
text-align: center;
margin-right: 0;
}
}
&.cell-hover {
background: #fafafa;
}
&.b-b:after {
left: 30rpx;
}
&.m-t {
margin-top: 16rpx;
}
@@ -119,22 +93,12 @@ page {
margin-top: 10rpx;
height: 60rpx;
display: flex;
justify-content: center; //这个是X轴居中
align-items: center; //这个是 Y轴居中
justify-content: center;
align-items: center;
align-self: baseline;
font-size: $font-lg;
color: $font-color-light;
margin-left: 10rpx;
}
.cell-tit {
flex: 1;
font-size: $font-base + 2rpx;
color: $font-color-dark;
margin-right: 10rpx;
}
.cell-tip {
font-size: $font-base;
color: $font-color-light;
}
}
</style>
</style>

View File

@@ -2,17 +2,15 @@
<view class="container">
<block v-for="(row, index) in messageList" :key="index">
<view class="msgItem">
<div class="is_read">
<!-- {{row.is_read}} -->
<span v-if="row.is_read"></span>
<span v-else class="red">·</span>
</div>
<div class="msgMsg">{{$u.timeFormat(row.send_time, 'yyyy-mm-dd')}}</div>
<u-card :title="title" :title-size="35" :border="false">
<div class="is_read">
<span v-if="row.is_read"></span>
<span v-else class="red">·</span>
</div>
<div class="msgMsg">{{ formatSendTime(row.send_time) }}</div>
<u-card :title="pageTitle" :title-size="35" :border="false">
<template #body>
<view class="u-body-item u-flex u-row-between u-p-b-0">
<view class="u-body-item-title u-line-2">{{row.content}}</view>
<view class="u-body-item-title u-line-2">{{ row.content }}</view>
</view>
</template>
</u-card>
@@ -22,74 +20,85 @@
</view>
</template>
<script>
import { mapMutations } from "vuex";
import * as API_Message from "@/api/message.js";
export default {
data() {
return {
title: "系统消息",
subTitle: "未读",
finished: false,
loadStatus: "more",
params: {
pageNumber: 0,
pageSize: 5
},
messageList: []
};
},
onLoad() {
this.GET_MessageList(true);
},
onReachBottom() {
this.params.pageNumber++;
this.GET_MessageList(false);
},
methods: {
...mapMutations(["logout"]),
/** 获取站内消息 */
GET_MessageList(reset) {
if (reset) {
this.params.pageNumber = 1;
this.messageList = [];
}
uni.showLoading({
title: "加载中"
});
API_Message.getMessages(this.params).then(async response => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
const { data } = response;
if (!data || !data.length) {
this.messageList.push(...data.data);
this.handleReadPageMessages();
}
});
},
/** 设置消息已读 **/
handleReadPageMessages() {
const ids = this.messageList.map(item => item.id).join(",");
API_Message.messageMarkAsRead(ids).then(async () => {});
}
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Message from '@/api/message.js'
const store = useStore()
const { proxy } = getCurrentInstance()!
const pageTitle = '系统消息'
const loadStatus = ref('more')
const queryParams = ref({ pageNumber: 1, pageSize: 5 })
const messageList = ref<any[]>([])
onLoad(() => {
fetchMessageList(true)
})
onReachBottom(() => {
queryParams.value.pageNumber++
fetchMessageList(false)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function formatSendTime(time: number) {
return proxy.$u.timeFormat(time, 'yyyy-mm-dd')
}
function extractRecords(res: any) {
if (Array.isArray(res?.result?.records)) return res.result.records
if (Array.isArray(res?.result)) return res.result
if (Array.isArray(res?.data)) return res.data
return []
}
function fetchMessageList(reset: boolean) {
if (reset) {
queryParams.value.pageNumber = 1
messageList.value = []
loadStatus.value = 'more'
}
};
uni.showLoading({ title: '加载中' })
API_Message.getMessages(queryParams.value).then((response) => {
hideLoadingIfNeeded()
const res = response.data
const records = extractRecords(res)
if (records.length) {
messageList.value.push(...records)
markPageMessagesAsRead()
} else {
loadStatus.value = 'noMore'
}
})
}
function markPageMessagesAsRead() {
const ids = messageList.value.map((item) => item.id).join(',')
if (!ids) return
API_Message.messageMarkAsRead(ids)
}
</script>
<style scoped lang='scss'>
.is_read{
position: absolute;
right: 25px;
top: 80rpx;
z-index: 999;
<style scoped lang="scss">
.is_read {
position: absolute;
right: 25px;
top: 80rpx;
z-index: 999;
}
.container {
background: #f9f9f9;
min-height: 100vh;
}
.red{
color: coral;
font-size: 100rpx;
.red {
color: coral;
font-size: 100rpx;
}
.msgMsg {
text-align: center;
@@ -99,4 +108,4 @@ export default {
padding: 1em 0;
position: relative;
}
</style>
</style>

View File

@@ -1,431 +1,492 @@
<template>
<view class="content">
<view class="page" :style="themeStyle">
<u-navbar
title="收藏"
:fixed="true"
:placeholder="true"
:border="false"
:auto-back="true"
title=""
>
<template #center>
<view class="slot-wrap">
<u-tabs
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
class="collect-tabs"
:list="navList"
:scrollable="true"
v-model:current="tabCurrentIndex"
></u-tabs>
</view>
</template>
</u-navbar>
<view class="collect-body">
<!-- 显示商品栏 -->
<view v-if="tabCurrentIndex == 0" class="tab-content">
<scroll-view class="list-scroll-content" scroll-y>
<u-empty style="margin-top: 40rpx" text="暂无收藏商品数据" mode="favor" v-if="goodsEmpty"></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in goodList"
:key="index"
class="collect-swipe"
>
<u-swipe-action-item
@open="openLeftChange(item, 'goods')"
:show="item.selected"
:options="LeftOptions"
@click="clickGoodsSwiperAction(item, index)"
:name="index"
>
<view class="goods" @click="goGoodsDetail(item)">
<u-image width="131rpx" height="131rpx" :src="item.image" mode="aspectFit">
<template #loading><u-loading></u-loading></template>
</u-image>
<view class="goods-intro">
<view class="goods-name">{{ item.goodsName }}</view>
<view class="goods-sn">{{ item.goods_sn }}</view>
<view class="goods-price">{{ unitPrice(item.price) }}</view>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
</template>
</scroll-view>
</view>
<!-- 显示收藏的店铺栏 -->
<view v-else class="tab-content">
<scroll-view class="list-scroll-content" scroll-y>
<u-empty style="margin-top: 40rpx" text="暂无收藏店铺数据" mode="favor" v-if="storeEmpty"></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in storeList"
:key="index"
class="collect-swipe"
>
<u-swipe-action-item
@open="openLeftChange(item, 'store')"
:show="item.selected"
:options="LeftOptions"
@click="clickStoreSwiperAction(item)"
:name="index"
>
<view class="store" @click="goStoreMainPage(item.id)">
<view class="intro">
<view class="store-logo">
<u-image width="102rpx" height="102rpx" :src="item.storeLogo" :alt="item.storeName"
mode="aspectFit">
<template #loading><u-loading></u-loading></template>
</u-image>
</view>
<view class="store-name">
<view>{{ item.storeName }}</view>
<u-tag size="mini" type="error" :color="$mainColor" v-if="item.selfOperated"
text="自营" mode="plain" shape="circle" />
</view>
<view class="store-collect">
<view>进店逛逛</view>
</view>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
</template>
</scroll-view>
</view>
></u-navbar>
<view class="tabs-wrap">
<u-tabs
:list="navList"
:scrollable="false"
:lineColor="lightColor"
:activeStyle="{ color: lightColor, fontWeight: '600' }"
:inactiveStyle="{ color: '#666' }"
v-model:current="tabCurrentIndex"
@change="onTabChange"
></u-tabs>
</view>
<scroll-view
class="list-scroll"
scroll-y
enable-back-to-top
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="loadMore"
>
<!-- 商品 -->
<view v-if="tabCurrentIndex === 0" class="tab-content">
<u-empty
v-if="!goodsLoading && goodsEmpty"
class="empty-box"
text="暂无收藏商品"
mode="favor"
></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in goodsList"
:key="item.skuId || item.goodsId || index"
class="collect-swipe"
>
<u-swipe-action-item
@open="openSwipeItem(item, 'goods')"
:show="item.selected"
:options="swipeOptions"
@click="removeGoodsCollection(item)"
:name="index"
>
<view class="goods" @click="goGoodsDetail(item)">
<u-image width="140rpx" height="140rpx" radius="12rpx" :src="item.image" mode="aspectFill">
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
<view class="goods-intro">
<view class="goods-name wes-2">{{ item.goodsName }}</view>
<view class="goods-price">{{ unitPrice(item.price) }}</view>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
<view class="load-status" v-if="goodsList.length">
{{ goodsFinished ? '没有更多了' : goodsLoading ? '加载中...' : '上拉加载更多' }}
</view>
</template>
</view>
<!-- 店铺 -->
<view v-else class="tab-content">
<u-empty
v-if="!storeLoading && storeEmpty"
class="empty-box"
text="暂无收藏店铺"
mode="favor"
></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in storeList"
:key="item.id || index"
class="collect-swipe"
>
<u-swipe-action-item
@open="openSwipeItem(item, 'store')"
:show="item.selected"
:options="swipeOptions"
@click="removeStoreCollection(item)"
:name="index"
>
<view class="store" @click="goStoreMainPage(item.id)">
<view class="store-logo">
<u-image
width="96rpx"
height="96rpx"
shape="circle"
:src="item.storeLogo"
mode="aspectFill"
>
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</view>
<view class="store-info">
<view class="store-name-row">
<text class="store-name wes">{{ item.storeName }}</text>
<u-tag
v-if="item.selfOperated"
size="mini"
type="error"
:color="mainColor"
text="自营"
plain
shape="circle"
/>
</view>
<view class="store-tip">左滑可取消收藏</view>
</view>
<view class="store-enter">进店逛逛</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
<view class="load-status" v-if="storeList.length">
{{ storeFinished ? '没有更多了' : storeLoading ? '加载中...' : '上拉加载更多' }}
</view>
</template>
</view>
</scroll-view>
</view>
</template>
<script>
import {
getGoodsCollection,
getStoreCollection,
deleteGoodsCollection,
deleteStoreCollection,
} from "@/api/members.js";
export default {
data() {
return {
lightColor:this.$lightColor,
// 商品左滑侧边栏
LeftOptions: [{
text: "取消",
style: {
backgroundColor: this.$lightColor,
},
}, ],
tabCurrentIndex: 0, //tab的下标默认为0也就是说会默认请求商品
navList: [
//tab显示数据
{
name: "商品(0)",
params: {
pageNumber: 1,
pageSize: 10,
},
},
{
name: "店铺(0)",
params: {
pageNumber: 1,
pageSize: 10,
},
},
],
goodsEmpty: false, //商品数据是否为空
storeEmpty: false, //店铺数据是否为空
goodList: [], //商品集合
storeList: [], //店铺集合
};
},
onShow() {
this.fetchReloadOrNextPage('reload')
},
onReachBottom() {
this.fetchReloadOrNextPage('next')
},
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { getThemeStyle } from '@/utils/theme'
import {
getGoodsCollection,
getStoreCollection,
deleteGoodsCollection,
deleteStoreCollection,
} from '@/api/members.js'
methods: {
// 刷新或者下一页
fetchReloadOrNextPage(type) {
if(type == 'next'){
this.navList[this.tabCurrentIndex].params.pageNumber ++;
if (this.tabCurrentIndex == 0) {
this.getGoodList();
} else {
this.getStoreList();
}
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const swipeOptions = computed(() => [
{
text: '取消',
style: { backgroundColor: lightColor.value },
},
])
const tabCurrentIndex = ref(0)
const navList = ref([
{ name: '商品(0)' },
{ name: '店铺(0)' },
])
const goodsParams = ref({ pageNumber: 1, pageSize: 10 })
const storeParams = ref({ pageNumber: 1, pageSize: 10 })
const goodsEmpty = ref(false)
const storeEmpty = ref(false)
const goodsList = ref<any[]>([])
const storeList = ref<any[]>([])
const goodsLoading = ref(false)
const storeLoading = ref(false)
const goodsFinished = ref(false)
const storeFinished = ref(false)
const refreshing = ref(false)
const inited = ref(false)
onShow(() => {
if (!inited.value) {
inited.value = true
reloadAll()
return
}
reloadCurrent()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function onTabChange(e: any) {
const index = typeof e === 'number' ? e : e?.index
if (typeof index === 'number') {
tabCurrentIndex.value = index
}
if (tabCurrentIndex.value === 0 && !goodsList.value.length && !goodsEmpty.value) {
reloadGoods()
}
if (tabCurrentIndex.value === 1 && !storeList.value.length && !storeEmpty.value) {
reloadStore()
}
}
function onRefresh() {
refreshing.value = true
reloadCurrent()
}
function loadMore() {
if (tabCurrentIndex.value === 0) {
if (goodsLoading.value || goodsFinished.value || goodsEmpty.value) return
goodsParams.value.pageNumber++
fetchGoodsList()
} else {
if (storeLoading.value || storeFinished.value || storeEmpty.value) return
storeParams.value.pageNumber++
fetchStoreList()
}
}
function reloadAll() {
reloadGoods()
reloadStore()
}
function reloadCurrent() {
if (tabCurrentIndex.value === 0) {
reloadGoods()
} else {
reloadStore()
}
}
function reloadGoods() {
goodsParams.value.pageNumber = 1
goodsFinished.value = false
goodsEmpty.value = false
goodsList.value = []
fetchGoodsList()
}
function reloadStore() {
storeParams.value.pageNumber = 1
storeFinished.value = false
storeEmpty.value = false
storeList.value = []
fetchStoreList()
}
function openSwipeItem(val: any, type: 'goods' | 'store') {
const targetList = type === 'goods' ? goodsList.value : storeList.value
targetList.forEach((item) => {
item.selected = false
})
val.selected = true
}
function removeGoodsCollection(val: any) {
deleteGoodsCollection(val.skuId).then((res) => {
if (res.statusCode === 200) {
reloadGoods()
}
})
}
function removeStoreCollection(val: any) {
deleteStoreCollection(val.id).then((res) => {
if (res.statusCode === 200) {
reloadStore()
}
})
}
function goGoodsDetail(val: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${val.skuId}&goodsId=${val.goodsId}`,
})
}
function goStoreMainPage(id: string) {
uni.navigateTo({
url: `/pages/product/shopPage?id=${id}`,
})
}
function fetchGoodsList() {
if (goodsLoading.value) return
goodsLoading.value = true
uni.showLoading({ title: '加载中' })
getGoodsCollection(goodsParams.value, 'GOODS')
.then((res) => {
goodsLoading.value = false
refreshing.value = false
hideLoadingIfNeeded()
if (res.data.success) {
const data = res.data.result
navList.value[0].name = `商品(${data.total || 0})`
goodsEmpty.value = !data.total
const records = (data.records || []).map((item: any) => ({
...item,
selected: false,
}))
if (goodsParams.value.pageNumber === 1) {
goodsList.value = records
} else {
goodsList.value.push(...records)
}
else{
this.navList[0].params.pageNumber = 1;
this.navList[1].params.pageNumber = 1;
this.goodList = [];
this.storeList = [];
this.getGoodList();
this.getStoreList();
}
},
/**
* 打开商品左侧取消收藏
*/
openLeftChange(val, type) {
const way = type === "goods" ? this.goodList : this.storeList;
way.forEach((item) => {
item.selected = false;
});
val.selected = true;
},
/**
* 点击商品左侧取消收藏
*/
clickGoodsSwiperAction(val) {
deleteGoodsCollection(val.skuId).then((res) => {
if (res.statusCode == 200) {
this.storeList = [];
this.goodList = [];
this.getGoodList();
}
});
},
/**
* 点击店铺左侧取消收藏
*/
clickStoreSwiperAction(val) {
deleteStoreCollection(val.id).then((res) => {
if (res.statusCode == 200) {
this.storeList = [];
this.getStoreList();
}
});
},
/**
* 查看商品详情
*/
goGoodsDetail(val) {
//商品详情
uni.navigateTo({
url: "/pages/product/goods?id=" + val.skuId + "&goodsId=" + val.goodsId,
});
},
/**
* 查看店铺详情
*/
goStoreMainPage(id) {
//店铺主页
uni.navigateTo({
url: "/pages/product/shopPage?id=" + id,
});
},
/**
* 获取商品集合
*/
getGoodList() {
uni.showLoading({
title: "加载中",
});
getGoodsCollection(this.navList[0].params, "GOODS").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
uni.stopPullDownRefresh();
if (res.data.success) {
let data = res.data.result;
data.selected = false;
this.navList[0].name = `商品(${data.total})`;
if (data.total == 0) {
this.goodsEmpty = true;
} else if (data.total < 10) {
this.goodsLoad = "noMore";
this.goodList.push(...data.records);
} else {
this.goodList.push(...data.records);
if (data.total.length < 10) this.goodsLoad = "noMore";
}
}
});
},
/**
* 获取店铺集合
*/
getStoreList() {
uni.showLoading({
title: "加载中",
});
getStoreCollection(this.navList[1].params, "STORE").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
uni.stopPullDownRefresh();
if (res.data.success) {
let data = res.data.result;
data.selected = false;
this.navList[1].name = `店铺(${data.total})`;
if (data.total == 0) {
this.storeEmpty = true;
} else if (data.total < 10) {
this.storeList.push(...data.records);
}
}
});
},
},
/**
* 下拉刷新时
*/
onPullDownRefresh() {
if (this.tabCurrentIndex == 0) {
this.navList[0].params.pageNumber = 1;
this.goodList = [];
this.getGoodList();
} else {
this.navList[1].params.pageNumber = 1;
this.storeList = [];
this.getStoreList();
goodsFinished.value =
!records.length || goodsList.value.length >= (data.total || 0)
}
},
};
})
.catch(() => {
goodsLoading.value = false
refreshing.value = false
hideLoadingIfNeeded()
})
}
function fetchStoreList() {
if (storeLoading.value) return
storeLoading.value = true
uni.showLoading({ title: '加载中' })
getStoreCollection(storeParams.value, 'STORE')
.then((res) => {
storeLoading.value = false
refreshing.value = false
hideLoadingIfNeeded()
if (res.data.success) {
const data = res.data.result
navList.value[1].name = `店铺(${data.total || 0})`
storeEmpty.value = !data.total
const records = (data.records || []).map((item: any) => ({
...item,
selected: false,
}))
if (storeParams.value.pageNumber === 1) {
storeList.value = records
} else {
storeList.value.push(...records)
}
storeFinished.value =
!records.length || storeList.value.length >= (data.total || 0)
}
})
.catch(() => {
storeLoading.value = false
refreshing.value = false
hideLoadingIfNeeded()
})
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
height: 100%;
}
page {
background: #f8f8f8;
height: 100%;
}
</style>
.content {
width: 100%;
<style lang="scss" scoped>
.page {
height: 100vh;
display: flex;
flex-direction: column;
background: #f8f8f8;
box-sizing: border-box;
}
.tabs-wrap {
flex-shrink: 0;
background: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.list-scroll {
flex: 1;
height: 0;
min-height: 0;
}
.tab-content {
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
}
.empty-box {
margin-top: 120rpx;
}
.collect-swipe,
:deep(.u-swipe-action),
:deep(.u-swipe-action-item),
:deep(.u-swipe-action-item__content) {
width: 100%;
}
:deep(.u-swipe-action-item__content) {
overflow: hidden;
}
.goods {
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
padding: 24rpx;
margin-top: 16rpx;
background: #fff;
.goods-intro {
flex: 1;
min-width: 0;
margin-left: 24rpx;
display: flex;
flex-direction: column;
min-height: 100vh;
justify-content: space-between;
min-height: 140rpx;
}
.slot-wrap {
flex: 1;
display: flex;
justify-content: center;
padding: 0 20rpx;
box-sizing: border-box;
.goods-name {
font-size: 28rpx;
color: #333;
line-height: 1.4;
}
.collect-tabs {
width: 100%;
.goods-price {
margin-top: 16rpx;
font-size: 32rpx;
font-weight: 600;
color: var(--theme-light, #ff6b35);
}
}
.collect-body {
flex: 1;
min-height: 0;
}
.store {
display: flex;
align-items: center;
box-sizing: border-box;
width: calc(100% - 32rpx);
margin: 16rpx auto 0;
padding: 24rpx;
background: #fff;
border-radius: 16rpx;
}
.tab-content {
height: 100%;
}
.store-logo {
flex-shrink: 0;
width: 96rpx;
height: 96rpx;
border-radius: 50%;
overflow: hidden;
background: #f5f5f5;
}
.list-scroll-content {
height: 100%;
width: 100%;
}
.store-info {
flex: 1;
min-width: 0;
margin: 0 20rpx;
}
.collect-swipe,
:deep(.u-swipe-action),
:deep(.u-swipe-action-item),
:deep(.u-swipe-action-item__content) {
width: 100%;
}
.store-name-row {
display: flex;
align-items: center;
gap: 12rpx;
}
:deep(.u-swipe-action-item__content) {
overflow: hidden;
}
.store-name {
max-width: 360rpx;
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.goods {
background-color: #fff;
border-bottom: 1px solid $border-color-light;
min-height: 190rpx;
box-sizing: border-box;
display: flex;
align-items: center;
padding: 30rpx 20rpx;
margin-top: 20rpx;
width: 100%;
.store-tip {
margin-top: 10rpx;
font-size: 22rpx;
color: #999;
}
.goods-intro {
flex: 1;
min-width: 0;
font-size: $font-base;
line-height: 48rpx;
margin-left: 30rpx;
.store-enter {
flex-shrink: 0;
padding: 12rpx 20rpx;
border-radius: 100px;
border: 1rpx solid var(--theme-light, #ff6b35);
color: var(--theme-light, #ff6b35);
font-size: 24rpx;
}
.goods-name {
line-height: 1.4em;
font-size: 24rpx;
max-height: 2.8em;
overflow: hidden;
color: #666;
}
.goods-sn {
color: #cccccc;
font-size: 24rpx;
}
.goods-price {
color: $light-color;
}
}
}
.store {
background-color: #fff;
border: 1px solid $border-color-light;
border-radius: 16rpx;
margin: 20rpx 10rpx;
width: calc(100% - 20rpx);
box-sizing: border-box;
.intro {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx 0 40rpx;
min-height: 170rpx;
.store-logo {
width: 102rpx;
height: 102rpx;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}
.store-name {
flex: 1;
min-width: 0;
margin-left: 30rpx;
line-height: 2em;
:first-child {
font-size: $font-base;
}
}
.store-collect {
flex-shrink: 0;
border-left: 1px solid $border-color-light;
padding-left: 20rpx;
text-align: center;
color: #999;
font-size: $font-sm;
}
}
}
.load-status {
padding: 24rpx 0 8rpx;
text-align: center;
font-size: 24rpx;
color: #999;
}
</style>

View File

@@ -7,14 +7,14 @@
:auto-back="true"
>
<template #right>
<div class="light-color edit" @click="isEdit = !isEdit">{{ !isEdit ? '编辑' : '完成'}}</div>
<view class="light-color edit" @click="isEdit = !isEdit">{{ !isEdit ? '编辑' : '完成'}}</view>
</template>
</u-navbar>
<view class="tracks-tip">
<u-icon name="volume" color="#f9ae3d" size="19"></u-icon>
<text class="tracks-tip-text">右划删除浏览记录</text>
</view>
<u-empty text="暂无历史记录" style="margin-top:200rpx;" mode="history" v-if="whetherEmpty"></u-empty>
<u-empty text="暂无历史记录" style="margin-top:200rpx;" mode="history" v-if="isEmpty"></u-empty>
<view v-else class="tracks-list">
<block v-for="(item, index) in trackList" :key="index">
<view
@@ -26,9 +26,9 @@
<u-swipe-action-item
:show="item.show"
:name="index"
@click="delTracks"
@open="open"
:options="options"
@click="deleteTracks"
@open="openSwipe"
:options="swipeOptions"
>
<view class="myTracks-item">
<u-checkbox-group v-if="isEdit" class="store-line-check">
@@ -37,7 +37,7 @@
shape="circle"
:active-color="lightColor"
v-model:checked="item.checked"
@change="checkboxChangeDP(item)"
@change="onTrackCheckChange(item)"
></u-checkbox>
</u-checkbox-group>
<view class="myTracks-item-img" @click.stop="navigateToDetail(item)">
@@ -56,7 +56,7 @@
</u-swipe-action>
<view class="myTracks-divider"></view>
</block>
<view v-if="isEdit" class="submit" @click="handleClickDeleteSelected">
<view v-if="isEdit" class="submit" @click="deleteSelectedTracks">
删除所选
</view>
</view>
@@ -64,172 +64,147 @@
</view>
</template>
<script>
import {
myTrackList,
deleteHistoryListId
} from "@/api/members.js";
import { getStoreBaseInfo } from "@/api/store.js";
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onShow, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { myTrackList, deleteHistoryListId } from '@/api/members.js'
import { getStoreBaseInfo } from '@/api/store.js'
export default {
data() {
return {
isEdit:false,
whetherEmpty: false, //是否数据为空
params: {
pageNumber: 1,
pageSize: 10,
order: "desc",
sort: "updateTime",
},
lightColor:this.$lightColor,
options: [{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}],
trackList: [], //足迹列表
storeNameMap: {},
};
},
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
/**
* 滑到底部加载下一页数据
*/
onReachBottom() {
this.params.pageNumber++;
this.getList();
},
onShow() {
this.params.pageNumber = 1
this.trackList = [];
this.getList();
},
onPullDownRefresh() {
this.trackList = [];
this.getList();
},
methods: {
getStoreName(item) {
return item.storeName || this.storeNameMap[item.storeId] || "";
},
async enrichStoreNames(records) {
const storeIds = [
...new Set(
records
.filter((item) => !item.storeName && item.storeId)
.map((item) => item.storeId)
),
].filter((storeId) => !this.storeNameMap[storeId]);
const isEdit = ref(false)
const isEmpty = ref(false)
const params = ref({
pageNumber: 1,
pageSize: 10,
order: 'desc',
sort: 'updateTime',
})
const swipeOptions = [{ text: '删除', style: { backgroundColor: '#dd524d' } }]
const trackList = ref<any[]>([])
const storeNameMap = ref<Record<string, string>>({})
await Promise.all(
storeIds.map(async (storeId) => {
try {
const res = await getStoreBaseInfo(storeId);
const name = res.data?.result?.storeName;
if (name) {
this.storeNameMap[storeId] = name;
}
} catch (e) {
// ignore
}
})
);
},
checkboxChangeDP(val){
console.log(val)
},
// 删除所选的数据
handleClickDeleteSelected(val){
const ids = this.trackList.filter(item=>item.checked).map(item=>item.goodsId);
if(!ids.length){
uni.showToast({
title:"请选择删除数据",
icon:"none"
})
}else{
this.delTracks(0,ids)
onReachBottom(() => {
params.value.pageNumber++
fetchTrackList()
})
onShow(() => {
params.value.pageNumber = 1
trackList.value = []
fetchTrackList()
})
onPullDownRefresh(() => {
params.value.pageNumber = 1
trackList.value = []
fetchTrackList()
})
function getStoreName(item: any) {
return item.storeName || storeNameMap.value[item.storeId] || ''
}
async function enrichStoreNames(records: any[]) {
const storeIds = [
...new Set(
records
.filter((item) => !item.storeName && item.storeId)
.map((item) => item.storeId)
),
].filter((storeId) => !storeNameMap.value[storeId])
await Promise.all(
storeIds.map(async (storeId) => {
try {
const res = await getStoreBaseInfo(storeId)
const name = res.data?.result?.storeName
if (name) {
storeNameMap.value[storeId] = name
}
},
/**
* 导航到店铺
*/
navigateToStore(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
open(index) {
// 先将正在被操作的swipeAction标记为打开状态否则由于props的特性限制
// 原本为'false',再次设置为'false'会无效
this.trackList[index].show = true;
this.trackList.map((val, idx) => {
if (index != idx) this.trackList[idx].show = false;
})
},
/**
* 跳转详情
*/
navigateToDetail(item) {
uni.navigateTo({
url: "/pages/product/goods?id=" + item.id + "&goodsId=" + item.goodsId,
});
},
} catch {
// ignore
}
})
)
}
/**
* 获取我的足迹列表
*/
getList() {
uni.showLoading({
title: "加载中",
});
myTrackList(this.params).then(async (res) => {
uni.stopPullDownRefresh();
uni.hideLoading();
if (res.statusCode == 200) {
const records = res.data.result.records || [];
records.forEach((item) => {
item.show = false;
item.checked = false;
});
function onTrackCheckChange(_val: any) {
// 勾选状态由 v-model:checked 维护
}
if (!records.length) {
if (this.trackList.length === 0) {
this.whetherEmpty = true;
}
} else {
await this.enrichStoreNames(records);
this.trackList.push(...records);
}
}
});
},
function deleteSelectedTracks() {
const ids = trackList.value.filter((item) => item.checked).map((item) => item.goodsId)
if (!ids.length) {
uni.showToast({ title: '请选择删除数据', icon: 'none' })
return
}
deleteTracks(0, ids)
}
function navigateToStore(val: any) {
uni.navigateTo({ url: `/pages/product/shopPage?id=${val.storeId}` })
}
/**
* 删除足迹
*/
delTracks(e, ids) {
const index = typeof e === 'object' ? (e.name ?? e.index) : e;
const goodsId = ids || this.trackList[index]?.goodsId;
if (!goodsId) return;
deleteHistoryListId(goodsId).then((res) => {
if (res.data.code == 200) {
this.trackList = [];
this.params.pageNumber = 1
this.getList();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
},
},
};
function openSwipe(index: number) {
trackList.value[index].show = true
trackList.value.forEach((val, idx) => {
if (index !== idx) val.show = false
})
}
function navigateToDetail(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
function fetchTrackList() {
uni.showLoading({ title: '加载中' })
myTrackList(params.value).then(async (res) => {
uni.stopPullDownRefresh()
uni.hideLoading()
if (res.statusCode === 200) {
const records = res.data.result.records || []
records.forEach((item: any) => {
item.show = false
item.checked = false
})
if (!records.length) {
if (trackList.value.length === 0) {
isEmpty.value = true
}
} else {
isEmpty.value = false
await enrichStoreNames(records)
trackList.value.push(...records)
}
}
})
}
function deleteTracks(e: any, ids?: any) {
const index = typeof e === 'object' ? (e.name ?? e.index) : e
const goodsId = ids || trackList.value[index]?.goodsId
if (!goodsId) return
deleteHistoryListId(goodsId).then((res) => {
if (res.data.code === 200) {
trackList.value = []
params.value.pageNumber = 1
fetchTrackList()
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
</script>
<style lang="scss" scoped>

View File

@@ -1,249 +1,245 @@
<template>
<view class="content">
<view class="portrait-box">
<image src="/static/pointTrade/point_bg_1.png" mode=""></image>
<image class="point-img" src="/static/pointTrade/tradehall.png" />
<view class="position-point">
</view>
</view>
<view class="point-summary">
<view class="point-summary-item">
<text>累计获得</text>
<text class="pcolor">{{ pointData.totalPoint || 0 }}</text>
</view>
<view class="point-summary-divider"></view>
<view class="point-summary-item">
<text>剩余积分</text>
<text class="pcolor">{{ pointData.point || 0 }}</text>
</view>
</view>
<div class="point-list">
<view class="point-item" v-for="(item, index) in pointList" :key="index">
<view class="point-item-left">
<view class="point-label">{{ item.content }}</view>
<view class="point-item-time">{{ item.createTime }}</view>
</view>
<view class="point-item-value" :class="[item.pointType == 'INCREASE' ? 'plus' : 'reduce']">
<text>{{ item.pointType == "INCREASE" ? "+" : "-" }}</text>{{ item.variablePoint }}
</view>
</view>
<uni-load-more :status="count.loadStatus"></uni-load-more>
</div>
</view>
</template>
<script>
import { getPointsData } from "@/api/members.js";
import { getMemberPointSum } from "@/api/members.js";
export default {
data() {
return {
count: {
loadStatus: "more",
},
pointList: [], //积分数据集合
params: {
pageNumber: 1,
pageSize: 10,
},
pointData: {}, //累计获取 未输入 集合
};
},
onLoad() {
this.initPointData();
this.getList();
},
/**
* 触底加载
*/
onReachBottom() {
this.params.pageNumber++;
this.getList();
},
methods: {
/**
* 获取积分数据
*/
getList() {
let params = this.params;
uni.showLoading({
title: "加载中",
});
getPointsData(params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (res.data.success) {
let data = res.data.result.records;
if (data.length < 10) {
this.count["loadStatus"] = "noMore";
this.pointList.push(...data);
} else {
this.pointList.push(...data);
if (data.length < 10) this.count["loadStatus"] = "noMore";
}
}
});
},
/**
* 获得累计积分使用
*/
initPointData() {
getMemberPointSum().then((res) => {
this.pointData = res.data.result;
});
},
},
};
</script>
<style lang="scss" scoped>
.point-list {
margin-top: 20rpx;
}
.title {
height: 80rpx;
text-align: center;
line-height: 80rpx;
font-size: 32rpx;
font-weight: bold;
}
.plus{
color: $light-color;
font-weight: bold;
}
.reduce{
color: $weChat-color;
font-weight: bold;
}
.point-item {
width: 100%;
min-height: 130rpx;
padding: 24rpx 20rpx;
background: #ffffff;
font-size: $font-sm;
border-bottom: 1px solid $border-color-light;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
.point-item-left {
flex: 1;
min-width: 0;
line-height: 40rpx;
}
.point-item-time {
color: #999;
}
.point-item-value {
flex-shrink: 0;
width: 100rpx;
text-align: center;
}
}
.point-summary {
display: flex;
align-items: center;
min-height: 100rpx;
padding: 28rpx 0;
background: #ffffff;
border-radius: 0 0 20rpx 20rpx;
margin: 0 20rpx;
font-size: 26rpx;
box-sizing: border-box;
.point-summary-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.point-summary-divider {
width: 1px;
height: 48rpx;
background: $border-color-light;
flex-shrink: 0;
}
.pcolor {
color: $light-color;
margin-left: 8rpx;
}
}
.content {
background: #f9f9f9;
}
.more {
text-align: right;
color: $u-tips-color;
font-size: 24rpx;
padding-right: 40rpx !important;
}
.portrait-box {
background-color: $main-color;
height: 250rpx;
background: linear-gradient(91deg, $light-color 1%, $aider-light-color 99%);
border-radius: 20rpx 20rpx 0 0;
margin: 20rpx 20rpx 0;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #ffffff;
> image:first-child {
width: 263rpx;
height: 250rpx;
position: absolute;
left: 0;
bottom: 0;
transform: rotateY(180deg);
}
.position-point {
position: absolute;
right: -2rpx;
top: 0;
.apply-point {
margin-top: 30rpx;
text-align: center;
line-height: 40rpx;
font-size: $font-sm;
color: #ffffff;
width: 142rpx;
height: 40rpx;
background: rgba(#ffffff, 0.2);
border-radius: 20rpx 0px 0px 20rpx;
}
}
.point-img {
height: 108rpx;
width: 108rpx;
margin-bottom: 30rpx;
}
.point {
font-size: 56rpx;
}
}
.point-label {
font-weight: bold;
margin-bottom: 10rpx;
color: #666666;
}
</style>
<template>
<view class="content">
<view class="portrait-box">
<image src="/static/pointTrade/point_bg_1.png" mode=""></image>
<image class="point-img" src="/static/pointTrade/tradehall.png" />
<view class="position-point">
</view>
</view>
<view class="point-summary">
<view class="point-summary-item">
<text>累计获得</text>
<text class="pcolor">{{ pointSummary.totalPoint || 0 }}</text>
</view>
<view class="point-summary-divider"></view>
<view class="point-summary-item">
<text>剩余积分</text>
<text class="pcolor">{{ pointSummary.point || 0 }}</text>
</view>
</view>
<view class="point-list">
<view class="point-item" v-for="(item, index) in pointList" :key="index">
<view class="point-item-left">
<view class="point-label">{{ item.content }}</view>
<view class="point-item-time">{{ item.createTime }}</view>
</view>
<view class="point-item-value" :class="[item.pointType == 'INCREASE' ? 'plus' : 'reduce']">
<text>{{ item.pointType == "INCREASE" ? "+" : "-" }}</text>{{ item.variablePoint }}
</view>
</view>
<uni-load-more :status="loadStatus"></uni-load-more>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getPointsData, getMemberPointSum } from '@/api/members.js'
const store = useStore()
const loadStatus = ref('more')
const pointList = ref<any[]>([])
const params = ref({ pageNumber: 1, pageSize: 10 })
const pointSummary = ref<Record<string, any>>({})
onLoad(() => {
fetchPointSummary()
fetchPointLogList()
})
onReachBottom(() => {
params.value.pageNumber++
fetchPointLogList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function fetchPointLogList() {
uni.showLoading({ title: '加载中' })
getPointsData(params.value).then((res) => {
hideLoadingIfNeeded()
if (res.data.success) {
const data = res.data.result.records || []
if (data.length < params.value.pageSize) {
loadStatus.value = 'noMore'
}
pointList.value.push(...data)
}
})
}
function fetchPointSummary() {
getMemberPointSum().then((res) => {
pointSummary.value = res.data.result
})
}
</script>
<style lang="scss">
page,
.content {
min-height: 100vh;
background: #f9f9f9;
}
.content {
overflow: hidden;
}
.point-list {
margin-top: 20rpx;
background: #f9f9f9;
min-height: calc(100vh - 390rpx);
padding-bottom: 24rpx;
box-sizing: border-box;
}
.title {
height: 80rpx;
text-align: center;
line-height: 80rpx;
font-size: 32rpx;
font-weight: bold;
}
.plus{
color: $light-color;
font-weight: bold;
}
.reduce{
color: $weChat-color;
font-weight: bold;
}
.point-item {
width: 100%;
min-height: 130rpx;
padding: 24rpx 20rpx;
background: #ffffff;
font-size: $font-sm;
border-bottom: 1px solid $border-color-light;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
.point-item-left {
flex: 1;
min-width: 0;
line-height: 40rpx;
}
.point-item-time {
color: #999;
}
.point-item-value {
flex-shrink: 0;
width: 100rpx;
text-align: center;
}
}
.point-summary {
display: flex;
align-items: center;
min-height: 100rpx;
padding: 28rpx 0;
background: #ffffff;
border-radius: 0 0 20rpx 20rpx;
margin: 0 20rpx;
font-size: 26rpx;
box-sizing: border-box;
.point-summary-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.point-summary-divider {
width: 1px;
height: 48rpx;
background: $border-color-light;
flex-shrink: 0;
}
.pcolor {
color: $light-color;
margin-left: 8rpx;
}
}
.more {
text-align: right;
color: $u-tips-color;
font-size: 24rpx;
padding-right: 40rpx !important;
}
.portrait-box {
background-color: $main-color;
height: 250rpx;
background: linear-gradient(91deg, $light-color 1%, $aider-light-color 99%);
border-radius: 20rpx 20rpx 0 0;
margin: 20rpx 20rpx 0;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #ffffff;
> image:first-child {
width: 263rpx;
height: 250rpx;
position: absolute;
left: 0;
bottom: 0;
transform: rotateY(180deg);
}
.position-point {
position: absolute;
right: -2rpx;
top: 0;
.apply-point {
margin-top: 30rpx;
text-align: center;
line-height: 40rpx;
font-size: $font-sm;
color: #ffffff;
width: 142rpx;
height: 40rpx;
background: rgba(#ffffff, 0.2);
border-radius: 20rpx 0px 0px 20rpx;
}
}
.point-img {
height: 108rpx;
width: 108rpx;
margin-bottom: 30rpx;
}
.point {
font-size: 56rpx;
}
}
.point-label {
font-weight: bold;
margin-bottom: 10rpx;
color: #666666;
}
.point-list .uni-load-more {
background: #f9f9f9;
}
</style>

View File

@@ -16,7 +16,7 @@
<u-cell-group class="cell-group" :border="false">
<!-- #ifdef APP-PLUS -->
<u-cell v-if="IosWhether" is-link title="去评分" @click="checkStar"></u-cell>
<u-cell v-if="showIosRating" is-link title="去评分" @click="openAppStoreRating"></u-cell>
<u-cell is-link title="功能介绍" @click="navigateTo('/pages/mine/set/versionFunctionList')"></u-cell>
<u-cell is-link title="检查更新" @click="checkUpdate"></u-cell>
<!-- #endif -->
@@ -40,93 +40,72 @@
</view>
</template>
<script>
import APPUpdate from "@/plugins/APPUpdate";
import config from "@/config/config";
import { getAppVersion } from "@/api/message.js";
export default {
data() {
return {
config,
IosWhether: false,
editionHistory: [],
versionData: {},
localVersion: "",
params: {
pageNumber: 1,
pageSize: 5,
},
};
},
onLoad() {
// #ifdef APP-PLUS
const platform = uni.getSystemInfoSync().platform;
if (platform === "android") {
this.params.type = 0;
} else {
this.IosWhether = true;
this.params.type = 1;
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import APPUpdate from '@/plugins/APPUpdate'
import config from '@/config/config'
import { getAppVersion } from '@/api/message.js'
const showIosRating = ref(false)
const versionData = ref<Record<string, any>>({})
const localVersion = ref<Record<string, any>>({})
const params = ref<Record<string, any>>({ pageNumber: 1, pageSize: 5 })
onLoad(() => {
// #ifdef APP-PLUS
const platform = uni.getSystemInfoSync().platform
if (platform === 'android') {
params.value.type = 0
} else {
showIosRating.value = true
params.value.type = 1
}
fetchRemoteVersion(platform)
plus.runtime.getProperty(plus.runtime.appid, (inf) => {
localVersion.value = {
versionCode: inf.version.replace(/\./g, ''),
version: inf.version,
}
this.getVersion(platform);
})
// #endif
plus.runtime.getProperty(plus.runtime.appid, (inf) => {
this.localVersion = {
versionCode: inf.version.replace(/\./g, ""),
version: inf.version,
};
});
// #endif
// #ifdef MP-WEIXIN
const accountInfo = wx.getAccountInfoSync()
localVersion.value = {
versionCode: accountInfo.miniProgram.version.replace(/\./g, ''),
version: accountInfo.miniProgram.version,
envVersion: accountInfo.miniProgram.envVersion,
}
// #endif
})
// #ifdef MP-WEIXIN
const accountInfo = wx.getAccountInfoSync();
this.version_number = accountInfo.miniProgram.version;
this.localVersion = {
versionCode: accountInfo.miniProgram.version.replace(/\./g, ""),
version: accountInfo.miniProgram.version,
envVersion: accountInfo.miniProgram.envVersion,
};
// #endif
},
async function fetchRemoteVersion(platform: string) {
const type = platform === 'android' ? 'ANDROID' : 'IOS'
const res = await getAppVersion(type)
if (res.data.success) {
versionData.value = res.data.result
}
}
methods: {
async getVersion(platform) {
let type;
platform == "android" ? (type = "ANDROID") : (type = "IOS");
function navigateTo(url: string) {
uni.navigateTo({ url })
}
let res = await getAppVersion(type);
if (res.data.success) {
this.versionData = res.data.result;
}
},
function openAppStoreRating() {
plus.runtime.launchApplication({
action: `itms-apps://itunes.apple.com/app/${config.iosAppId}?action=write-review`,
})
}
navigateTo(url) {
uni.navigateTo({
url,
});
},
checkStar() {
plus.runtime.launchApplication({
action: `itms-apps://itunes.apple.com/app/${config.iosAppId}?action=write-review`,
});
},
checkUpdate() {
if (
this.versionData.version.replace(/\./g, "") <
this.localVersion.versionCode
) {
APPUpdate();
} else {
uni.showToast({
title: "当前版本已是最新版",
duration: 2000,
icon: "none",
});
}
},
},
};
function checkUpdate() {
if (versionData.value.version?.replace(/\./g, '') < localVersion.value.versionCode) {
APPUpdate()
} else {
uni.showToast({ title: '当前版本已是最新版', duration: 2000, icon: 'none' })
}
}
</script>
<style lang="scss" scoped>

View File

@@ -4,9 +4,9 @@
<view class="box-title">猜你想问</view>
<view
class="feedBack-item"
:class="{ active: feedBack.type == item.value }"
@click="handleClick(index)"
v-for="(item, index) in list"
:class="{ active: feedbackForm.type == item.value }"
@click="selectFeedbackType(index)"
v-for="(item, index) in feedbackTypeList"
:key="index"
>
{{ item.text }}
@@ -15,11 +15,11 @@
<view class="feedBack-box">
<view class="box-title">问题反馈
<text class="box-tag" v-if="feedBack.type">@{{ list.find(item => item.value == feedBack.type).text }}</text>
<text class="box-tag" v-if="feedbackForm.type">@{{ feedbackTypeList.find(item => item.value == feedbackForm.type)?.text }}</text>
</view>
<u-textarea
class="field-textarea"
v-model="feedBack.context"
v-model="feedbackForm.context"
placeholder="请输入反馈信息"
border="none"
height="240"
@@ -39,7 +39,7 @@
<view class="box-title">手机号</view>
<u-input
class="field-input"
v-model="feedBack.mobile"
v-model="feedbackForm.mobile"
type="number"
maxlength="11"
border="none"
@@ -48,98 +48,69 @@
></u-input>
</view>
<view class="submit" @click="submit()">提交</view>
<view class="submit" @click="submitFeedback">提交</view>
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import config from "@/config/config";
import { feedBack } from "@/api/members.js";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
export default {
data() {
return {
storage,
config,
feedBack: {
type: "FUNCTION",
context: "",
mobile: "",
},
inputStyle: {
background: "#fafafa",
borderRadius: "12rpx",
padding: "0 24rpx",
height: "80rpx",
},
uploadFileList: [],
list: [
{ text: "功能相关", value: "FUNCTION" },
{ text: "优化反馈", value: "OPTIMIZE" },
{ text: "其他", value: "OTHER" },
],
};
},
methods: {
// 点击反馈内容
handleClick(index) {
this.feedBack["type"] = this.list[index].value;
},
<script setup lang="ts">
import { ref, reactive, getCurrentInstance } from 'vue'
import { feedBack as submitFeedBackApi } from '@/api/members.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.uploadFileList, (urls) => {
this.feedBack.images = urls.join(",");
});
},
const { proxy } = getCurrentInstance()!
/**
* 提交意见反馈
*/
submit() {
if (!this.feedBack.type) {
uni.showToast({
title: "请填写反馈类型",
duration: 2000,
icon: "none",
});
return false;
}
if (!this.feedBack.context) {
uni.showToast({
title: "请填写反馈信息",
duration: 2000,
icon: "none",
});
return false;
}
if (this.feedBack.mobile && !this.$u.test.mobile(this.feedBack.mobile)) {
uni.showToast({
title: "请填写您的正确手机号",
duration: 2000,
icon: "none",
});
return false;
}
/** 提交 */
feedBack(this.feedBack).then((res) => {
if (res.data.success) {
uni.showToast({
title: "提交成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 500);
}
});
},
},
};
const feedbackForm = reactive({
type: 'FUNCTION',
context: '',
mobile: '',
images: '',
})
const inputStyle = {
background: '#fafafa',
borderRadius: '12rpx',
padding: '0 24rpx',
height: '80rpx',
}
const uploadFileList = ref<any[]>([])
const feedbackTypeList = [
{ text: '功能相关', value: 'FUNCTION' },
{ text: '优化反馈', value: 'OPTIMIZE' },
{ text: '其他', value: 'OTHER' },
]
function selectFeedbackType(index: number) {
feedbackForm.type = feedbackTypeList[index].value
}
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, uploadFileList.value, (urls) => {
feedbackForm.images = urls.join(',')
})
}
function submitFeedback() {
if (!feedbackForm.type) {
uni.showToast({ title: '请填写反馈类型', duration: 2000, icon: 'none' })
return
}
if (!feedbackForm.context) {
uni.showToast({ title: '请填写反馈信息', duration: 2000, icon: 'none' })
return
}
if (feedbackForm.mobile && !proxy.$u.test.mobile(feedbackForm.mobile)) {
uni.showToast({ title: '请填写您的正确手机号', duration: 2000, icon: 'none' })
return
}
submitFeedBackApi(feedbackForm).then((res) => {
if (res.data.success) {
uni.showToast({ title: '提交成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 500)
}
})
}
</script>
<style lang="scss" scoped>

View File

@@ -5,48 +5,54 @@
<view>点击修改头像</view>
</view>
<u-form :model="form" ref="uForm" class="form">
<u-form-item label="昵称" label-width="150" :border-bottom="true">
<up-form
:model="form"
ref="uForm"
class="form"
label-position="left"
label-width="180rpx"
>
<up-form-item label="昵称" label-width="180rpx" :border-bottom="true">
<u-input
v-model="form.nickName"
border="none"
input-align="right"
placeholder="请输入昵称"
/>
</u-form-item>
</up-form-item>
<u-form-item label="性别" label-width="150" :border-bottom="true">
<up-form-item label="性别" label-width="180rpx" :border-bottom="true">
<view class="sex-row">
<u-radio-group v-model="form.sex" :active-color="lightColor" :gap="40">
<u-radio name="1" label="男" shape="circle"></u-radio>
<u-radio name="0" label="女" shape="circle"></u-radio>
</u-radio-group>
</view>
</u-form-item>
</up-form-item>
<u-form-item label="生日" label-width="150" :border-bottom="true">
<up-form-item label="生日" label-width="180rpx" :border-bottom="true">
<view class="form-value" @click="showBirthday = true">
{{ birthday || '请选择出生日期' }}
</view>
<template #right>
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</template>
</u-form-item>
</up-form-item>
<u-form-item label="城市" label-width="150" :border-bottom="true">
<up-form-item label="城市" label-width="180rpx" :border-bottom="true">
<view class="form-value" @click="clickRegion">
{{ form.___path || '请选择城市' }}
{{ form.regionPath || '请选择城市' }}
</view>
<template #right>
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</template>
</u-form-item>
</up-form-item>
<u-form-item label="手机号" label-width="150" :border-bottom="false">
<up-form-item label="手机号" label-width="180rpx" :border-bottom="false">
<view v-if="form.mobile" class="form-value">{{ form.mobile }}</view>
<view v-else class="bind-mobile" @click="navigateTo(form.username)">绑定手机号码</view>
</u-form-item>
</u-form>
<view v-else class="bind-mobile" @click="navigateToBindMobile(form.username)">绑定手机号码</view>
</up-form-item>
</up-form>
<view class="bottom">
<view class="submit" @click="submit">保存</view>
@@ -74,152 +80,135 @@
</view>
</template>
<script>
import { saveUserInfo } from "@/api/members.js";
import { upload } from "@/api/common.js";
import storage from "@/utils/storage.js";
import city from "@/components/m-city/m-city.vue";
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { quiteLoginOut } from '@/utils/filters.js'
import { saveUserInfo } from '@/api/members.js'
import { upload } from '@/api/common.js'
import storage from '@/utils/storage.js'
import MCity from '@/components/m-city/m-city.vue'
function parseBirthdayTimestamp(str) {
if (!str) {
return Date.now();
}
const timestamp = Date.parse(String(str).replace(/-/g, "/"));
return Number.isFinite(timestamp) ? timestamp : Date.now();
function parseBirthdayTimestamp(str?: string) {
if (!str) return Date.now()
const timestamp = Date.parse(String(str).replace(/-/g, '/'))
return Number.isFinite(timestamp) ? timestamp : Date.now()
}
function formatBirthday(timestamp) {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
function formatBirthday(timestamp: number) {
const date = new Date(timestamp)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
export default {
components: { "m-city": city },
data() {
const userInfo = storage.getUserInfo() || {};
return {
lightColor: this.$lightColor,
form: {
nickName: userInfo.nickName || "",
birthday: userInfo.birthday || "",
face: userInfo.face || "/static/missing-face.png",
regionId: [],
region: userInfo.region || [],
sex: userInfo.sex != null ? String(userInfo.sex) : "1",
___path: userInfo.region,
mobile: userInfo.mobile,
username: userInfo.username,
},
birthday: userInfo.birthday || "",
birthdayValue: parseBirthdayTimestamp(userInfo.birthday),
minBirthdayDate: new Date("1950-01-01").getTime(),
maxBirthdayDate: Date.now(),
region: [
{
id: "",
localName: "请选择",
children: [],
},
],
showBirthday: false,
};
},
methods: {
getPickerParentValue(e) {
this.form.region = [];
this.form.regionId = [];
let name = "";
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
e.forEach((item, index) => {
if (item.id) {
this.form.region.push(item.localName);
this.form.regionId.push(item.id);
if (index == e.length - 1) {
name += item.localName;
} else {
name += item.localName + ",";
}
this.form.___path = name;
}
});
},
const userInfo = storage.getUserInfo() || {}
const form = reactive<Record<string, any>>({
nickName: userInfo.nickName || '',
birthday: userInfo.birthday || '',
face: userInfo.face || '/static/missing-face.png',
regionId: [],
region: userInfo.region || [],
sex: userInfo.sex != null ? String(userInfo.sex) : '1',
regionPath: userInfo.region,
mobile: userInfo.mobile,
username: userInfo.username,
})
const birthday = ref(userInfo.birthday || '')
const birthdayValue = ref(parseBirthdayTimestamp(userInfo.birthday))
const minBirthdayDate = new Date('1950-01-01').getTime()
const maxBirthdayDate = Date.now()
const region = ref([{ id: '', localName: '请选择', children: [] }])
const showBirthday = ref(false)
const cityPicker = ref<any>(null)
clickRegion() {
this.$refs.cityPicker.show();
},
function getPickerParentValue(selected: any[]) {
form.region = []
form.regionId = []
let name = ''
selected.forEach((item, index) => {
if (item.id) {
form.region.push(item.localName)
form.regionId.push(item.id)
name += index === selected.length - 1 ? item.localName : `${item.localName},`
form.regionPath = name
}
})
}
submit() {
delete this.form.___path;
const params = JSON.parse(JSON.stringify(this.form));
saveUserInfo(params).then((res) => {
if (res.statusCode == 200) {
storage.setUserInfo(res.data.result);
uni.navigateBack();
}
});
},
function clickRegion() {
cityPicker.value?.show()
}
changeFace() {
uni.chooseImage({
success: (chooseImageRes) => {
const tempFilePaths = chooseImageRes.tempFilePaths;
uni.uploadFile({
url: upload,
filePath: tempFilePaths[0],
name: "file",
header: {
accessToken: storage.getAccessToken(),
},
success: (uploadFileRes) => {
const data = JSON.parse(uploadFileRes.data);
this.form.face = data.result;
},
});
},
});
},
function submit() {
const params = JSON.parse(JSON.stringify(form))
delete params.regionPath
saveUserInfo(params).then((res) => {
if (res.statusCode === 200) {
storage.setUserInfo(res.data.result)
uni.navigateBack()
}
})
}
selectTime(e) {
const timestamp = typeof e?.value === "number" ? e.value : this.birthdayValue;
const formatted = formatBirthday(timestamp);
this.birthdayValue = timestamp;
this.form.birthday = formatted;
this.birthday = formatted;
this.showBirthday = false;
},
function changeFace() {
uni.chooseImage({
success: (chooseImageRes) => {
uni.uploadFile({
url: upload,
filePath: chooseImageRes.tempFilePaths[0],
name: 'file',
header: { accessToken: storage.getAccessToken() },
success: (uploadFileRes) => {
const data = JSON.parse(uploadFileRes.data)
form.face = data.result
},
})
},
})
}
closeBirthdayPicker() {
this.showBirthday = false;
},
function selectTime(e: any) {
const timestamp = typeof e?.value === 'number' ? e.value : birthdayValue.value
const formatted = formatBirthday(timestamp)
birthdayValue.value = timestamp
form.birthday = formatted
birthday.value = formatted
showBirthday.value = false
}
navigateTo(username) {
uni.navigateTo({
url: "/pages/mine/set/securityCenter/bindMobile?username=" + username,
});
},
function closeBirthdayPicker() {
showBirthday.value = false
}
syncUserInfo() {
const userInfo = storage.getUserInfo() || {};
this.form.nickName = userInfo.nickName || "";
this.form.birthday = userInfo.birthday || "";
this.form.face = userInfo.face || "/static/missing-face.png";
this.form.region = userInfo.region || [];
this.form.sex = userInfo.sex != null ? String(userInfo.sex) : "1";
this.form.___path = userInfo.region;
this.form.mobile = userInfo.mobile;
this.form.username = userInfo.username;
this.birthday = userInfo.birthday || "";
this.birthdayValue = parseBirthdayTimestamp(userInfo.birthday);
},
},
function navigateToBindMobile(username: string) {
uni.navigateTo({
url: `/pages/mine/set/securityCenter/bindMobile?username=${username}`,
})
}
onShow() {
this.syncUserInfo();
},
};
function syncUserInfo() {
const latest = storage.getUserInfo() || {}
form.nickName = latest.nickName || ''
form.birthday = latest.birthday || ''
form.face = latest.face || '/static/missing-face.png'
form.region = latest.region || []
form.sex = latest.sex != null ? String(latest.sex) : '1'
form.regionPath = latest.region
form.mobile = latest.mobile
form.username = latest.username
birthday.value = latest.birthday || ''
birthdayValue.value = parseBirthdayTimestamp(latest.birthday)
}
onShow(() => {
syncUserInfo()
})
</script>
<style lang="scss" scoped>

View File

@@ -1,227 +1,211 @@
<template>
<view class="box">
<view class="box-tips">
<h2 class='h2'>
绑定手机号码
</h2>
<h2 class="h2">绑定手机号码</h2>
<view class="verification"></view>
</view>
<view class="form">
<u-form :model="codeForm" ref="validateCodeForm">
<view v-if="!validateFlage">
<u-form-item label-width="120" label="手机号" prop="mobile">
<up-form
:model="codeForm"
ref="validateCodeForm"
label-position="left"
label-width="180rpx"
>
<view v-if="!phoneVerified">
<up-form-item label-width="180rpx" label="手机号" prop="mobile">
<u-input maxlength="11" v-model="codeForm.mobile" placeholder="请输入您的手机号" />
</u-form-item>
<u-form-item class="sendCode" label-width="120" prop="code" label="验证码">
<u-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code unique-key="page-edit" :seconds="seconds" @end="end" @start="start"
ref="uCode" @change="codeChange"></u-code>
<view @tap="getCode" class="text-tips">{{ tips }}</view>
</u-form-item>
</up-form-item>
<view class="submit" @click="validatePhone">绑定</view>
<myVerification keep-running @send="verification" class="verification" ref="verification"
business="BIND_MOBILE" />
<up-form-item class="sendCode" label-width="180rpx" prop="code" label="验证码">
<u-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code
unique-key="page-edit"
:seconds="seconds"
@end="onCodeCountdownEnd"
@start="onCodeCountdownStart"
ref="uCodeRef"
@change="onCodeTextChange"
></u-code>
<view @tap="requestSmsCode" class="text-tips">{{ codeTips }}</view>
</up-form-item>
<view class="submit" @click="submitBindMobile">绑定</view>
<myVerification
keep-running
@send="onVerificationPassed"
class="verification"
ref="verificationRef"
business="BIND_MOBILE"
/>
</view>
</u-form>
</up-form>
</view>
</view>
</template>
<script>
import {
sendMobile,
bindMobile
} from "@/api/login";
import { getUserInfo } from "@/api/members.js";
import storage from "@/utils/storage.js";
<script setup lang="ts">
import { ref, reactive, watch, getCurrentInstance } from 'vue'
import { onLoad, onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { sendMobile, bindMobile } from '@/api/login'
import { getUserInfo } from '@/api/members.js'
import storage from '@/utils/storage.js'
import MyVerification from '@/components/verification/verification.vue'
import myVerification from "@/components/verification/verification.vue"; //验证
import uuid from "@/utils/uuid.modified.js";
export default {
components: {
myVerification,
const store = useStore()
const { proxy } = getCurrentInstance()!
const phoneVerified = ref(false)
const verificationPassed = ref(false)
const codeForm = reactive({
mobile: '',
code: '',
username: '',
})
const codeTips = ref('')
const seconds = 69
const validateCodeForm = ref<any>(null)
const uCodeRef = ref<any>(null)
const verificationRef = ref<any>(null)
const codeRules = {
mobile: [
{
validator: (_rule: any, value: string) => proxy.$u.test.mobile(value),
message: '手机号码不正确',
trigger: ['blur'],
},
data() {
return {
uuid,
validateFlage: false, //是否进行了手机号验证
step: 0, //当前验证步骤
flage: false, //是否验证码验证
codeForm: {
mobile: "", //手机号
code: "", //验证码
username: "", //用户名
},
tips: "", //提示
seconds: 69, // 60s等待时间
// 验证码登录校验
codeRules: {
mobile: [{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["blur"],
}, ],
code: [{
min: 4,
max: 6,
required: true,
message: "请输入验证码",
trigger: ["blur"],
}, ],
},
};
},
onLoad(options) {
this.codeForm.username = options.username;
},
onReady() {
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
this.$refs.validateCodeForm.setRules(this.codeRules);
},
watch: {
flage(val) {
if (val) {
if (this.$refs.uCode.canGetCode) {
uni.showLoading({
title: "正在获取验证码",
});
sendMobile(this.codeForm.mobile, "BIND_MOBILE").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
// 这里此提示会被this.start()方法中的提示覆盖
if (res.data.success) {
this.$refs.uCode.start();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.flage = false;
this.$refs.verification.getCode();
}
})
} else {
this.$u.toast("请倒计时结束后再发送");
}
}
},
],
code: [
{
min: 4,
max: 6,
required: true,
message: '请输入验证码',
trigger: ['blur'],
},
],
}
methods: {
// 验证码验证
verification(val) {
this.flage = val == this.$store.state.verificationKey ? true : false;
},
onLoad((options) => {
codeForm.username = options?.username || ''
})
// 验证手机号
validatePhone() {
this.$refs.validateCodeForm.validate((valid) => {
if (valid) {
bindMobile(this.codeForm).then((res) => {
if (res.data.success) {
this.validateFlage = !this.validateFlage;
// 获取最新的用户信息并更新缓存
getUserInfo().then(userRes => {
if (userRes.data.success) {
storage.setUserInfo(userRes.data.result);
}
// 显示成功提示
uni.showToast({
title: "绑定成功!",
duration: 2000,
icon: "none",
});
// 返回上一页
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 1000);
});
}
});
}
});
},
onReady(() => {
validateCodeForm.value?.setRules(codeRules)
})
codeChange(text) {
this.tips = text;
},
end() {
this.flage = false;
this.$refs.verification.getCode()
},
/**获取验证码 */
getCode() {
if (this.tips == "重新获取") {
this.$refs.verification.error(); //发送
}
if (!this.$u.test.mobile(this.codeForm.mobile)) {
uni.showToast({
title: "请输入正确手机号",
icon: "none",
});
return false;
}
if (!this.flage) {
this.$refs.verification.error(); //发送
return false;
}
},
start() {
this.$u.toast("验证码已发送");
this.flage = true;
this.$refs.verification.hide();
},
},
};
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
watch(verificationPassed, (val) => {
if (!val) return
if (!uCodeRef.value?.canGetCode) {
proxy.$u.toast('请倒计时结束后再发送')
return
}
.sendCode {
::v-deep .u-form-item--right__content__slot {
display: flex;
uni.showLoading({ title: '正在获取验证码' })
sendMobile(codeForm.mobile, 'BIND_MOBILE').then((res) => {
if (store.state.isShowToast) uni.hideLoading()
if (res.data.success) {
uCodeRef.value?.start()
} else {
uni.showToast({ title: res.data.message, duration: 2000, icon: 'none' })
verificationPassed.value = false
verificationRef.value?.getCode()
}
}
})
})
.h2 {
font-size: 40rpx;
font-weight: bold;
}
function onVerificationPassed(val: string) {
verificationPassed.value = val === store.state.verificationKey
}
page {
background: #fff;
}
function submitBindMobile() {
validateCodeForm.value?.validate((valid: boolean) => {
if (!valid) return
bindMobile(codeForm).then((res) => {
if (res.data.success) {
phoneVerified.value = true
getUserInfo().then((userRes) => {
if (userRes.data.success) {
storage.setUserInfo(userRes.data.result)
}
uni.showToast({ title: '绑定成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
})
}
})
})
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
function onCodeTextChange(text: string) {
codeTips.value = text
}
.submit {
background: $light-color;
}
function onCodeCountdownEnd() {
verificationPassed.value = false
verificationRef.value?.getCode()
}
.box-tips {
margin: 0 72rpx;
function requestSmsCode() {
if (codeTips.value === '重新获取') {
verificationRef.value?.error()
}
if (!proxy.$u.test.mobile(codeForm.mobile)) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (!verificationPassed.value) {
verificationRef.value?.error()
}
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
function onCodeCountdownStart() {
proxy.$u.toast('验证码已发送')
verificationPassed.value = true
verificationRef.value?.hide()
}
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
}
.sendCode {
::v-deep .u-form-item__body__right__content__slot {
display: flex;
}
}
.h2 {
font-size: 40rpx;
font-weight: bold;
}
page {
background: #fff;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.submit {
background: $light-color;
}
.box-tips {
margin: 0 72rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

View File

@@ -1,160 +1,99 @@
<template>
<view class="box">
<view class="box-tips">
<h2 class='h2'>
{{verificationTitle.title}}
</h2>
<view class="verification">{{verificationTitle.desc}}</view>
<h2 class="h2">{{ verificationTitle.title }}</h2>
<view class="verification">{{ verificationTitle.desc }}</view>
</view>
<view class="form">
<u-form :model="codeForm" ref="validateCodeForm">
<u-form-item label-width="120" label="旧密码">
<u-input type="password" v-model="oldPassword" placeholder="请输入您的旧密码" />
</u-form-item>
<up-form
:model="codeForm"
ref="validateCodeForm"
label-position="left"
label-width="180rpx"
>
<up-form-item label-width="180rpx" label="旧密码">
<u-input type="password" v-model="oldPassword" placeholder="请输入您的旧密码" />
</up-form-item>
<u-form-item label-width="120" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" />
</u-form-item>
<up-form-item label-width="180rpx" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" />
</up-form-item>
<u-form-item label-width="120" label="确认密码">
<u-input type="password" v-model="newPassword" placeholder="请再次输入您的密码" />
</u-form-item>
<up-form-item label-width="180rpx" label="确认密码">
<u-input type="password" v-model="confirmPassword" placeholder="请再次输入您的密码" />
</up-form-item>
<view class="submit" @click="updatePassword">修改密码</view>
</u-form>
<view class="submit" @click="updatePassword">修改密码</view>
</up-form>
</view>
</view>
</template>
<script>
import {
resetByMobile,
modifyPass
} from "@/api/login";
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { modifyPass } from '@/api/login'
import { md5 } from '@/utils/md5.js'
import {
md5
} from "@/utils/md5.js"; // md5
import myVerification from "@/components/verification/verification.vue"; //验证
import uuid from "@/utils/uuid.modified.js";
export default {
components: {
myVerification,
},
data() {
return {
uuid,
validateFlage: false, //是否进行了手机号验证
verificationTitle: {
title: "修改密码",
desc: "请验证并输入密码",
},
step: 0, //当前验证步骤
flage: false, //是否验证码验证
const verificationTitle = {
title: '修改密码',
desc: '请验证并输入密码',
}
const codeForm = reactive({ mobile: '', code: '' })
const oldPassword = ref('')
const password = ref('')
const confirmPassword = ref('')
codeForm: {
mobile: "", //手机号
code: "", //验证码
},
newPassword: "", //新密码
password: "", //密码
oldPassword: '', //旧密码
tips: "", //提示
seconds: 69, // 60s等待时间
// 验证码登录校验
codeRules: {
mobile: [{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["blur"],
}, ],
code: [{
min: 4,
max: 6,
required: true,
message: "请输入验证码",
trigger: ["blur"],
}, ],
},
};
},
onReady() {
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
this.$refs.validateCodeForm.setRules(this.codeRules);
},
methods: {
// 修改密码
updatePassword() {
if(this.password !== this.newPassword){
uni.showToast({
title: "两次输入密码不一致!",
icon: "none",
});
return;
}
modifyPass({
password: md5(this.oldPassword),
newPassword: md5(this.newPassword),
}).then((res) => {
if (res.data.success) {
uni.showToast({
title: "修改成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 1000);
}
});
},
},
};
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
function updatePassword() {
if (password.value !== confirmPassword.value) {
uni.showToast({ title: '两次输入密码不一致!', icon: 'none' })
return
}
.sendCode {
::v-deep .u-form-item--right__content__slot {
display: flex;
modifyPass({
password: md5(oldPassword.value),
newPassword: md5(password.value),
}).then((res) => {
if (res.data.success) {
uni.showToast({ title: '修改成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
}
})
}
</script>
.h2 {
font-size: 40rpx;
font-weight: bold;
}
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
page {
background: #fff;
}
::v-deep .u-form-item {
margin: 40rpx 0;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.h2 {
font-size: 40rpx;
font-weight: bold;
}
.submit {
background: $light-color;
}
page {
background: #fff;
}
.box-tips {
margin: 0 72rpx;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
.submit {
background: $light-color;
}
.box-tips {
margin: 0 72rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

View File

@@ -1,281 +1,238 @@
<template>
<view class="box">
<view class="box-tips">
<h2 class='h2'>
{{verificationTitle[validateFlage==false ? 0 : 1].title}}
</h2>
<view class="verification">{{verificationTitle[step].desc}}</view>
<h2 class="h2">{{ verificationTitle[phoneVerified ? 1 : 0].title }}</h2>
<view class="verification">{{ verificationTitle[step].desc }}</view>
</view>
<view class="form">
<u-form :model="codeForm" ref="validateCodeForm">
<view v-if="!validateFlage">
<u-form-item label-width="120" label="手机号" prop="mobile">
<up-form
:model="codeForm"
ref="validateCodeForm"
label-position="left"
label-width="180rpx"
>
<view v-if="!phoneVerified">
<up-form-item label-width="180rpx" label="手机号" prop="mobile">
<u-input maxlength="11" v-model="codeForm.mobile" placeholder="请输入您的手机号" />
</u-form-item>
</up-form-item>
<u-form-item class="sendCode" label-width="120" prop="code" label="验证码">
<up-form-item class="sendCode" label-width="180rpx" prop="code" label="验证码">
<u-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code unique-key="page-edit" :seconds="seconds" @end="end" @start="start"
ref="uCode" @change="codeChange"></u-code>
<view @tap="getCode" class="text-tips">{{ tips }}</view>
</u-form-item>
<u-code
unique-key="page-edit"
:seconds="seconds"
@end="onCodeCountdownEnd"
@start="onCodeCountdownStart"
ref="uCodeRef"
@change="onCodeTextChange"
></u-code>
<view @tap="requestSmsCode" class="text-tips">{{ codeTips }}</view>
</up-form-item>
<view class="submit" @click="validatePhone">验证</view>
<myVerification keep-running @send="verification" class="verification" ref="verification"
business="FIND_USER" />
<view class="submit" @click="verifyMobile">验证</view>
<myVerification
keep-running
@send="onVerificationPassed"
class="verification"
ref="verificationRef"
business="FIND_USER"
/>
</view>
<view v-if="validateFlage">
<u-form-item label-width="120" label="密码">
<view v-if="phoneVerified">
<up-form-item label-width="180rpx" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" />
</u-form-item>
</up-form-item>
<u-form-item label-width="120" label="确认密码">
<u-input type="password" v-model="newPassword" placeholder="请再次输入您的密码" />
</u-form-item>
<up-form-item label-width="180rpx" label="确认密码">
<u-input type="password" v-model="confirmPassword" placeholder="请再次输入您的密码" />
</up-form-item>
<view class="submit" @click="updatePassword">修改密码</view>
</view>
</u-form>
</up-form>
</view>
</view>
</template>
<script>
import {
sendMobile,
resetByMobile,
resetPassword
} from "@/api/login";
<script setup lang="ts">
import { ref, reactive, watch, getCurrentInstance } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { isLogin } from '@/utils/filters.js'
import { sendMobile, resetByMobile, resetPassword } from '@/api/login'
import { md5 } from '@/utils/md5.js'
import MyVerification from '@/components/verification/verification.vue'
import {
md5
} from "@/utils/md5.js"; // md5
import myVerification from "@/components/verification/verification.vue"; //验证
import uuid from "@/utils/uuid.modified.js";
export default {
components: {
myVerification,
const store = useStore()
const { proxy } = getCurrentInstance()!
const phoneVerified = ref(false)
const verificationPassed = ref(false)
const verificationTitle = [
{ title: '安全验证', desc: '请输入当前手机号进行安全验证' },
{ title: '修改密码', desc: '请输入新密码' },
]
const step = ref(0)
const codeForm = reactive({ mobile: '', code: '' })
const password = ref('')
const confirmPassword = ref('')
const codeTips = ref('')
const seconds = 69
const validateCodeForm = ref<any>(null)
const uCodeRef = ref<any>(null)
const verificationRef = ref<any>(null)
const codeRules = {
mobile: [
{
validator: (_rule: any, value: string) => proxy.$u.test.mobile(value),
message: '手机号码不正确',
trigger: ['blur'],
},
data() {
return {
uuid,
validateFlage: false, //是否进行了手机号验证
verificationTitle: [{
title: "安全验证",
desc: "请输入当前手机号进行安全验证",
},
{
title: "修改密码",
desc: "请输入新密码",
},
],
step: 0, //当前验证步骤
flage: false, //是否验证码验证
codeForm: {
mobile: "", //手机号
code: "", //验证码
},
newPassword: "", //新密码
password: "", //密码
tips: "", //提示
seconds: 69, // 60s等待时间
// 验证码登录校验
codeRules: {
mobile: [{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["blur"],
}, ],
code: [{
min: 4,
max: 6,
required: true,
message: "请输入验证码",
trigger: ["blur"],
}, ],
},
};
],
code: [
{
min: 4,
max: 6,
required: true,
message: '请输入验证码',
trigger: ['blur'],
},
onReady() {
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
this.$refs.validateCodeForm.setRules(this.codeRules);
},
watch: {
flage(val) {
if (val) {
],
}
if (this.$refs.uCode.canGetCode) {
uni.showLoading({
title: "正在获取验证码",
});
sendMobile(this.codeForm.mobile, "FIND_USER").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
// 这里此提示会被this.start()方法中的提示覆盖
if (res.data.success) {
this.$refs.uCode.start();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.flage = false;
this.$refs.verification.getCode();
}
})
} else {
this.$u.toast("请倒计时结束后再发送");
}
}
},
},
onReady(() => {
validateCodeForm.value?.setRules(codeRules)
})
methods: {
// 修改密码
updatePassword() {
if(this.password !== this.newPassword){
uni.showToast({
title: "两次输入密码不一致!",
icon: "none",
});
return;
}
resetPassword({
password: md5(this.password),
}).then((res) => {
if (res.data.success) {
uni.showToast({
title: "修改成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 1000);
}
});
},
// 验证码验证
verification(val) {
this.flage = val == this.$store.state.verificationKey ? true : false;
},
// 验证手机号
validatePhone() {
this.$refs.validateCodeForm.validate((valid) => {
if (valid) {
resetByMobile(this.codeForm).then((res) => {
if (res.data.success) {
this.validateFlage = !this.validateFlage;
// 登录成功
uni.showToast({
title: "验证成功!",
icon: "none",
});
}
});
}
});
},
codeChange(text) {
this.tips = text;
},
end() {
this.flage = false;
this.$refs.verification.getCode()
},
/**判断是否是当前用户的手机号 */
isUserPhone() {
let flage = false;
let user = this.isLogin();
if (user.mobile != this.codeForm.mobile) {
uni.showToast({
title: "请输入当前绑定手机号",
icon: "none",
});
flage = false;
} else {
flage = true;
}
return flage;
},
/**获取验证码 */
getCode() {
if (this.isUserPhone()) {
if (this.tips == "重新获取") {
this.$refs.verification.error(); //发送
}
if (!this.$u.test.mobile(this.codeForm.mobile)) {
uni.showToast({
title: "请输入正确手机号",
icon: "none",
});
return false;
}
if (!this.flage) {
this.$refs.verification.error(); //发送
return false;
}
}
},
start() {
this.$u.toast("验证码已发送");
this.flage = true;
this.$refs.verification.hide();
},
},
};
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
watch(verificationPassed, (val) => {
if (!val) return
if (!uCodeRef.value?.canGetCode) {
proxy.$u.toast('请倒计时结束后再发送')
return
}
.sendCode {
::v-deep .u-form-item--right__content__slot {
display: flex;
uni.showLoading({ title: '正在获取验证码' })
sendMobile(codeForm.mobile, 'FIND_USER').then((res) => {
if (store.state.isShowToast) uni.hideLoading()
if (res.data.success) {
uCodeRef.value?.start()
} else {
uni.showToast({ title: res.data.message, duration: 2000, icon: 'none' })
verificationPassed.value = false
verificationRef.value?.getCode()
}
}
})
})
.h2 {
font-size: 40rpx;
font-weight: bold;
}
function onVerificationPassed(val: string) {
verificationPassed.value = val === store.state.verificationKey
}
page {
background: #fff;
function updatePassword() {
if (password.value !== confirmPassword.value) {
uni.showToast({ title: '两次输入密码不一致!', icon: 'none' })
return
}
resetPassword({ password: md5(password.value) }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '修改成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
})
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
function verifyMobile() {
validateCodeForm.value?.validate((valid: boolean) => {
if (!valid) return
resetByMobile(codeForm).then((res) => {
if (res.data.success) {
phoneVerified.value = true
uni.showToast({ title: '验证成功!', icon: 'none' })
}
})
})
}
.submit {
background: $light-color;
}
function onCodeTextChange(text: string) {
codeTips.value = text
}
.box-tips {
margin: 0 72rpx;
}
function onCodeCountdownEnd() {
verificationPassed.value = false
verificationRef.value?.getCode()
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
function isCurrentUserPhone() {
const user = isLogin()
if (user?.mobile !== codeForm.mobile) {
uni.showToast({ title: '请输入当前绑定手机号', icon: 'none' })
return false
}
return true
}
function requestSmsCode() {
if (!isCurrentUserPhone()) return
if (codeTips.value === '重新获取') {
verificationRef.value?.error()
}
if (!proxy.$u.test.mobile(codeForm.mobile)) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (!verificationPassed.value) {
verificationRef.value?.error()
}
}
function onCodeCountdownStart() {
proxy.$u.toast('验证码已发送')
verificationPassed.value = true
verificationRef.value?.hide()
}
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
}
.sendCode {
::v-deep .u-form-item__body__right__content__slot {
display: flex;
}
}
.h2 {
font-size: 40rpx;
font-weight: bold;
}
page {
background: #fff;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.submit {
background: $light-color;
}
.box-tips {
margin: 0 72rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

View File

@@ -4,7 +4,7 @@
<u-cell-group>
<u-cell class="border-top" :isLink="false" title="面容登录">
<template #right-icon>
<u-switch @change="faceSwitchChange" active-color="#1abc9c" size="40" v-model="checked"></u-switch>
<u-switch @change="onFaceSwitchChange" active-color="#1abc9c" size="40" v-model="enabled"></u-switch>
</template>
</u-cell>
</u-cell-group>
@@ -12,74 +12,65 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { setBiolofy } from "@/api/passport.js";
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import storage from '@/utils/storage.js'
import { setBiolofy } from '@/api/passport.js'
export default {
data() {
return {
lightColor: this.$lightColor,
checked: true,
};
},
methods: {
faceSwitchChange(value) {
if (value === true) {
const res = uni.getSystemInfoSync();
plus.device.getInfo({
success: function (e) {
let params = {
mobile_type: res.model,
secret_key: e.uuid,
};
setBiolofy(params).then((res) => {
if (res.statusCode === 200) {
storage.setFaceLogin(true);
}
});
},
fail: function (e) {
//plus.nativeUI.toast('获取设备信息错误:' + JSON.stringify(e));
console.error("getDeviceInfo failed: " + JSON.stringify(e));
},
});
} else {
storage.setFaceLogin(false);
const enabled = ref(false)
onLoad(() => {
// #ifdef APP-PLUS
uni.checkIsSupportSoterAuthentication({
success(res) {
if (!res.supportMode.find((e) => e === 'facial')) {
plus.nativeUI.toast('此设备不支持面部识别')
uni.navigateBack()
}
uni.checkIsSoterEnrolledInDevice({
checkAuthMode: 'facial',
success(_res) {
if (!_res.isEnrolled) {
plus.nativeUI.toast('此设备未录入面部信息')
uni.navigateBack()
}
},
fail() {
uni.navigateBack()
},
})
},
},
onLoad() {
// #ifdef APP-PLUS
uni.checkIsSupportSoterAuthentication({
success(res) {
if (!res.supportMode.find((e) => e === "facial")) {
plus.nativeUI.toast("此设备不支持面部识别");
uni.navigateBack();
}
uni.checkIsSoterEnrolledInDevice({
checkAuthMode: "facial",
success(_res) {
if (!_res.isEnrolled) {
plus.nativeUI.toast("此设备未录入面部信息");
uni.navigateBack();
}
},
fail(_err) {
// plus.nativeUI.toast(JSON.stringify(_err));
uni.navigateBack();
},
});
fail() {
uni.navigateBack()
},
})
enabled.value = storage.getFaceLogin() || false
// #endif
})
function onFaceSwitchChange(value: boolean) {
if (value) {
const systemInfo = uni.getSystemInfoSync()
plus.device.getInfo({
success(e) {
setBiolofy({
mobile_type: systemInfo.model,
secret_key: e.uuid,
}).then((res) => {
if (res.statusCode === 200) {
storage.setFaceLogin(true)
}
})
},
fail(err) {
// plus.nativeUI.toast(JSON.stringify(err));
uni.navigateBack();
console.error('getDeviceInfo failed: ' + JSON.stringify(err))
},
});
this.checked = storage.getFaceLogin() || false;
// #endif
},
};
})
} else {
storage.setFaceLogin(false)
}
}
</script>
<style lang="scss" scoped>

View File

@@ -4,7 +4,7 @@
<u-cell-group>
<u-cell class="border-top" :isLink="false" title="指纹登录">
<template #right-icon>
<u-switch @change="fingerSwitchChange" :active-color="lightColor" size="40" v-model="checked"></u-switch>
<u-switch @change="onFingerSwitchChange" :active-color="lightColor" size="40" v-model="enabled"></u-switch>
</template>
</u-cell>
</u-cell-group>
@@ -12,60 +12,57 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { setBiolofy } from "@/api/passport.js";
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import storage from '@/utils/storage.js'
import { setBiolofy } from '@/api/passport.js'
export default {
data() {
return {
lightColor: this.$lightColor,
checked: false,
};
},
methods: {
fingerSwitchChange(value) {
if (value === true) {
const res = uni.getSystemInfoSync();
plus.device.getInfo({
success: function (e) {
let params = {
mobile_type: res.model,
secret_key: e.uuid,
};
setBiolofy(params).then((res) => {
if (res.statusCode === 200) {
storage.setFingerLogin(true);
}
});
},
fail: function (e) {
console.error("getDeviceInfo failed: " + JSON.stringify(e));
},
});
} else {
storage.setFingerLogin(false);
}
},
},
onLoad() {
// #ifdef APP-PLUS
if (!plus.fingerprint.isSupport()) {
plus.nativeUI.toast("此设备不支持指纹识别");
uni.navigateBack();
}
if (!plus.fingerprint.isKeyguardSecure()) {
plus.nativeUI.toast("此设备未设置密码锁屏");
uni.navigateBack();
}
if (!plus.fingerprint.isEnrolledFingerprints()) {
plus.nativeUI.toast("此设备未录入指纹");
uni.navigateBack();
}
this.checked = storage.getFingerLogin() || false;
// #endif
},
};
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const enabled = ref(false)
onLoad(() => {
// #ifdef APP-PLUS
if (!plus.fingerprint.isSupport()) {
plus.nativeUI.toast('此设备不支持指纹识别')
uni.navigateBack()
}
if (!plus.fingerprint.isKeyguardSecure()) {
plus.nativeUI.toast('此设备未设置密码锁屏')
uni.navigateBack()
}
if (!plus.fingerprint.isEnrolledFingerprints()) {
plus.nativeUI.toast('此设备未录入指纹')
uni.navigateBack()
}
enabled.value = storage.getFingerLogin() || false
// #endif
})
function onFingerSwitchChange(value: boolean) {
if (value) {
const systemInfo = uni.getSystemInfoSync()
plus.device.getInfo({
success(e) {
setBiolofy({
mobile_type: systemInfo.model,
secret_key: e.uuid,
}).then((res) => {
if (res.statusCode === 200) {
storage.setFingerLogin(true)
}
})
},
fail(err) {
console.error('getDeviceInfo failed: ' + JSON.stringify(err))
},
})
} else {
storage.setFingerLogin(false)
}
}
</script>
<style scoped lang="scss">

View File

@@ -2,56 +2,44 @@
<view class="securityCenter">
<u-cell-group>
<u-cell title="修改密码" @click="navigateTo('/pages/mine/set/securityCenter/updatePwdTab')"></u-cell>
<u-cell title="注销账户" @click="zhuxiao"></u-cell>
<u-cell title="注销账户" @click="confirmAccountDeletion"></u-cell>
</u-cell-group>
</view>
</template>
<script>
export default {
data() {
return {
mobile: "", //存储手机号
};
},
<script setup lang="ts">
function navigateTo(url: string) {
uni.navigateTo({ url })
}
methods: {
zhuxiao(){
uni.showModal({
title: "警告",
content: "您确定要注销当前账号吗?",
confirmText: "确定注销",
confirmColor: "#FF0000",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
uni.showModal({
title: "谨慎操作",
content: "再次向您确认,您确定要注销当前账号吗?",
confirmText: "坚持注销",
confirmColor: "#FF0000",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
uni.showToast({
title: "您的注销申请已经提交,待管理员审核后。会自动注销当前账号",
duration: 10000,
});
}
},
});
}
},
});
function confirmAccountDeletion() {
uni.showModal({
title: '警告',
content: '您确定要注销当前账号吗?',
confirmText: '确定注销',
confirmColor: '#FF0000',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
uni.showModal({
title: '谨慎操作',
content: '再次向您确认,您确定要注销当前账号吗?',
confirmText: '坚持注销',
confirmColor: '#FF0000',
cancelText: '取消',
success: (confirmRes) => {
if (confirmRes.confirm) {
uni.showToast({
title: '您的注销申请已经提交,待管理员审核后。会自动注销当前账号',
duration: 10000,
})
}
},
})
}
},
navigateTo(url) {
uni.navigateTo({
url: url,
});
},
},
};
})
}
</script>
<style lang="scss" scoped>

View File

@@ -7,22 +7,10 @@
</view>
</template>
<script>
export default {
data() {
return {
mobile: "", //存储手机号
};
},
methods: {
navigateTo(url) {
uni.navigateTo({
url: url,
});
},
},
};
<script setup lang="ts">
function navigateTo(url: string) {
uni.navigateTo({ url })
}
</script>
<style lang="scss" scoped>

View File

@@ -38,112 +38,95 @@
</view>
</template>
<script>
import config from "@/config/config";
export default {
data() {
return {
config,
userImage: config.defaultUserPhoto,
isCertificate: false,
userInfo: {},
fileSizeString: "0B",
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import config from '@/config/config'
import { isLogin, tipsToLogin, quiteLoginOut, logoff } from '@/utils/filters.js'
methods: {
navigateTo(url) {
if (url == "/pages/set/securityCenter/securityCenter") {
url += `?mobile=${this.userInfo.mobile}`;
}
uni.navigateTo({
url: url,
});
},
const userImage = config.defaultUserPhoto
const userInfo = ref<Record<string, any>>({})
const fileSizeString = ref('0B')
getCacheSize() {
let that = this;
plus.cache.calculate(function (size) {
let sizeCache = parseInt(size);
if (sizeCache == 0) {
that.fileSizeString = "0B";
} else if (sizeCache < 1024) {
that.fileSizeString = sizeCache + "B";
} else if (sizeCache < 1048576) {
that.fileSizeString = (sizeCache / 1024).toFixed(2) + "KB";
} else if (sizeCache < 1073741824) {
that.fileSizeString = (sizeCache / 1048576).toFixed(2) + "MB";
} else {
that.fileSizeString = (sizeCache / 1073741824).toFixed(2) + "GB";
}
});
},
onShow(() => {
userInfo.value = isLogin() || {}
// #ifdef APP-PLUS
getCacheSize()
// #endif
})
checkUserInfo() {
if (this.isLogin("auth")) {
this.navigateTo("/pages/mine/set/personMsg");
} else {
this.tipsToLogin();
}
},
function navigateTo(url: string) {
if (url === '/pages/mine/set/securityCenter/securityCenter') {
url += `?mobile=${userInfo.value.mobile || ''}`
}
uni.navigateTo({ url })
}
clearCache() {
let that = this;
let os = plus.os.name;
if (os == "Android") {
let main = plus.android.runtimeMainActivity();
let sdRoot = main.getCacheDir();
let files = plus.android.invoke(sdRoot, "listFiles");
let len = files.length;
for (let i = 0; i < len; i++) {
let filePath = "" + files[i];
plus.io.resolveLocalFileSystemURL(
filePath,
function (entry) {
if (entry.isDirectory) {
entry.removeRecursively(
function () {
uni.showToast({
title: "缓存清理完成",
duration: 2000,
icon: "none",
});
that.getCacheSize();
},
function () {}
);
} else {
entry.remove();
}
},
function () {
uni.showToast({
title: "文件路径读取失败",
duration: 2000,
icon: "none",
});
}
);
}
} else {
plus.cache.clear(function () {
uni.showToast({
title: "缓存清理完成",
duration: 2000,
icon: "none",
});
that.getCacheSize();
});
}
},
},
onShow() {
this.userInfo = this.isLogin();
// #ifdef APP-PLUS
this.getCacheSize();
// #endif
},
};
function getCacheSize() {
// #ifdef APP-PLUS
plus.cache.calculate((size) => {
const sizeCache = parseInt(String(size))
if (sizeCache === 0) {
fileSizeString.value = '0B'
} else if (sizeCache < 1024) {
fileSizeString.value = `${sizeCache}B`
} else if (sizeCache < 1048576) {
fileSizeString.value = `${(sizeCache / 1024).toFixed(2)}KB`
} else if (sizeCache < 1073741824) {
fileSizeString.value = `${(sizeCache / 1048576).toFixed(2)}MB`
} else {
fileSizeString.value = `${(sizeCache / 1073741824).toFixed(2)}GB`
}
})
// #endif
}
function checkUserInfo() {
if (isLogin('auth')) {
navigateTo('/pages/mine/set/personMsg')
} else {
tipsToLogin()
}
}
function clearCache() {
// #ifdef APP-PLUS
const os = plus.os.name
if (os === 'Android') {
const main = plus.android.runtimeMainActivity()
const sdRoot = main.getCacheDir()
const files = plus.android.invoke(sdRoot, 'listFiles')
const len = files.length
for (let i = 0; i < len; i++) {
const filePath = `${files[i]}`
plus.io.resolveLocalFileSystemURL(
filePath,
(entry) => {
if (entry.isDirectory) {
entry.removeRecursively(
() => {
uni.showToast({ title: '缓存清理完成', duration: 2000, icon: 'none' })
getCacheSize()
},
() => {}
)
} else {
entry.remove()
}
},
() => {
uni.showToast({ title: '文件路径读取失败', duration: 2000, icon: 'none' })
}
)
}
} else {
plus.cache.clear(() => {
uni.showToast({ title: '缓存清理完成', duration: 2000, icon: 'none' })
getCacheSize()
})
}
// #endif
}
</script>
<style lang="scss" scoped>

View File

@@ -1,58 +1,45 @@
<template>
<div>
<u-collapse v-if="versionData.length !=0">
<u-collapse-item class="version-item" :title="item.versionName" v-for="(item, index) in versionData" :key="index">
<!-- {{item.body}} -->
{{item.content}}
<u-collapse v-if="versionList.length !== 0">
<u-collapse-item
class="version-item"
:title="item.versionName"
v-for="(item, index) in versionList"
:key="index"
>
{{ item.content }}
</u-collapse-item>
</u-collapse>
<u-empty class="empty" v-else text="暂无版本信息" mode="list"></u-empty>
</div>
</template>
<script>
import { getAppVersionList } from "@/api/message";
export default {
data() {
return {
versionData: [],
appType: "",
params: {
pageNumber: 1,
pageSize: 10,
},
};
},
mounted() {
const platform = uni.getSystemInfoSync().platform;
/**
* 获取是否是安卓
*/
if (platform === "android") {
this.appType = "ANDROID";
} else {
this.IosWhether = true;
this.appType = "IOS";
}
this.getVersionList();
},
methods: {
async getVersionList() {
let res = await getAppVersionList(this.appType, this.params);
if (res.data.success) {
this.versionData = res.data.result.records;
}
},
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getAppVersionList } from '@/api/message'
const versionList = ref<any[]>([])
const appType = ref('')
const params = { pageNumber: 1, pageSize: 10 }
onLoad(() => {
const platform = uni.getSystemInfoSync().platform
appType.value = platform === 'android' ? 'ANDROID' : 'IOS'
fetchVersionList()
})
async function fetchVersionList() {
const res = await getAppVersionList(appType.value, params)
if (res.data.success) {
versionList.value = res.data.result.records
}
}
</script>
<style scoped lang="scss">
.version-item{
padding: 10rpx;
background: #fff;
.version-item {
padding: 10rpx;
background: #fff;
}
</style>
</style>

View File

@@ -3,10 +3,10 @@
<view class="date-card">
<div class="box">
<div class="circle-box">
<div class="cricle" @click="signIn()">
<span v-if="!ifSign" :class="{ active: signFlag || ifSign }">签到</span>
<span v-else :class="{ active: signFlag || ifSign }"
:style="ifSign ? 'transform: rotateY(0deg);' : ''">已签</span>
<div class="cricle" @click="handleSignIn()">
<span v-if="!hasSignedToday" :class="{ active: signAnimating || hasSignedToday }">签到</span>
<span v-else :class="{ active: signAnimating || hasSignedToday }"
:style="hasSignedToday ? 'transform: rotateY(0deg);' : ''">已签</span>
</div>
</div>
<text class="tips">坚持每天连续签到可以获多重奖励哦</text>
@@ -22,19 +22,19 @@
<view class="week">
<text v-for="item in weekArr" :key="item.id">{{ item }}</text>
</view>
<view class="date" v-for="obj in dataObj" :key="obj.id">
<view class="item" v-for="item in obj" :key="item.id" :class="item == '' ? 'hide' : ''"
<view class="date" v-for="(obj, rowIndex) in calendarRows" :key="rowIndex">
<view class="item" v-for="(item, dayIndex) in obj" :key="dayIndex" :class="item == '' ? 'hide' : ''"
:animation="item == currentDay ? animationData : ''">
<view class="just" :class="signArr.indexOf(item) != -1 ? 'active' : ''">
<view class="just" :class="signedDays.indexOf(item) != -1 ? 'active' : ''">
<view class="top">{{ item }} </view>
<view class="bottom">
<u-icon name="error" v-if="item <= currentDay" size="24" color="#999"></u-icon>
</view>
</view>
<view class="back" :class="signArr.indexOf(item) != -1 ? 'active' : ''" :style="
signArr.indexOf(item) != -1 && ifSign
<view class="back" :class="signedDays.indexOf(item) != -1 ? 'active' : ''" :style="
signedDays.indexOf(item) != -1 && hasSignedToday
? 'transform: rotateY(0deg);'
: signArr.indexOf(item) != -1 && item != currentDay
: signedDays.indexOf(item) != -1 && item != currentDay
? 'transform: rotateY(0deg);'
: ''
">
@@ -47,11 +47,11 @@
</view>
</view>
</div>
<view class="mask" :class="{ show: maskFlag, trans: transFlag }" ref="mask">
<view class="mask" :class="{ show: showSuccessMask, trans: maskClosing }" ref="mask">
<view class="mask-header">
<text class="close"></text>
<text>签到成功</text>
<text class="close" @click="close">×</text>
<text class="close" @click="closeSuccessMask">×</text>
</view>
<view class="mask-con">
<u-icon size="120" style="margin: 50rpx 0" :color="aiderLightColor" name="checkmark"></u-icon>
@@ -61,231 +61,147 @@
</view>
</template>
<script>
import { sign, signTime } from "@/api/point.js";
export default {
data() {
return {
aiderLightColor:this.$aiderLightColor,
signFlag: false,
animationData: {},
maskFlag: false, //
transFlag: false, //动画
weekArr: ["日", "一", "二", "三", "四", "五", "六"], //周数组
dateArr: [], //每个月的天数
monthArr: [
//实例化每个月
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
], //今天一个月英文
currentMonth: "", //当月
currentMonthIndex: "", //当月
currentYear: "", //今年
currentDay: "", //今天
currentWeek: "", //获取当月一号是周几
dataObj: [], //一个月有多少天这个获取
signArr: [], //本月签到过的天数 该参数用于请求接口后获取当月都哪天签到了
signAll: [], //所有签到数据
ifSign: false, //今天是否签到
};
},
async onLoad() {
//获取签到数据
var response = await signTime(
new Date().getFullYear() + "" + this.makeUp(new Date().getMonth() + 1)
);
this.signAll = response.data.result;
//获取展示数据
this.getDate();
},
methods: {
/**
* 补0
*/
makeUp(val) {
if (val >= 10) {
return val;
} else {
return "0" + val;
}
},
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { sign, signTime } from '@/api/point.js'
/**
* 点击签到
*/
async signIn() {
await sign().then((response) => {
if (this.ifSign) return;
if (this.signFlag) return;
if (response.data.code != 200) {
uni.showToast({
title: response.data.message,
duration: 2000,
icon: "none",
});
const store = useStore()
const aiderLightColor = computed(() => store.getters.aiderLightColor)
return false;
}
var that = this;
var animation = uni.createAnimation({
duration: 200,
timingFunction: "linear",
});
this.signArr.push(this.currentDay);
this.animation = animation;
animation.rotateY(0).step();
this.animationData = animation.export();
const signAnimating = ref(false)
const animationData = ref({})
const showSuccessMask = ref(false)
const maskClosing = ref(false)
const weekArr = ['日', '一', '二', '三', '四', '五', '六']
const monthLabels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
const currentMonth = ref('')
const currentMonthIndex = ref(0)
const currentYear = ref(0)
const currentDay = ref(0)
const calendarRows = ref<any[][]>([])
const signedDays = ref<number[]>([])
const signRecords = ref<any[]>([])
const hasSignedToday = ref(false)
setTimeout(
function () {
that.signFlag = true;
this.maskFlag = true;
this.ifSign = !this.ifSign;
animation.rotateY(0).step();
this.animationData = animation.export();
}.bind(this),
200
);
});
},
onLoad(async () => {
const response = await signTime(
`${new Date().getFullYear()}${padZero(new Date().getMonth() + 1)}`
)
signRecords.value = response.data.result
buildCalendar()
})
/**
* 签到成功后关闭弹窗
*/
close() {
var that = this;
this.maskFlag = false;
this.transFlag = true;
setTimeout(() => {
that.transFlag = false;
}, 500);
},
function padZero(val: number) {
return val >= 10 ? String(val) : `0${val}`
}
/**
* 获取今天时间
*
*/
getDate() {
var date = new Date(),
index = date.getMonth(),
curDay = null;
this.currentYear = date.getFullYear();
this.currentMonth = this.monthArr[index];
this.currentMonthIndex = index + 1;
this.currentDay = date.getDate();
if (this.currentDay == this.signArr[this.signArr.length - 1]) {
this.ifSign = true;
}
curDay = this.getWeekByDay(this.currentYear + "-" + (index + 1) + "-1");
this.getMonthDays(index, curDay);
this.curentSignData();
},
async function handleSignIn() {
if (hasSignedToday.value || signAnimating.value) return
/**
* 获取当前已经签到的时间
*/
curentSignData() {
var date = new Date(),
index = date.getMonth(),
curDay = null;
this.signArr = [];
for (var i = 0; i < this.signAll.length; i++) {
var item = this.signAll[i];
item.createTime = item.createTime.split(" ")[0];
var itemVal = item.createTime.split("-");
if (
Number(itemVal[0]) === Number(this.currentYear) &&
Number(itemVal[1]) === Number(this.currentMonthIndex)
) {
this.signArr.push(Number(itemVal[2]));
}
if (
Number(itemVal[0]) === Number(date.getFullYear()) &&
Number(itemVal[1]) === Number(index + 1) &&
Number(itemVal[2]) === Number(date.getDate())
) {
this.ifSign = true;
}
}
},
const response = await sign()
if (response.data.code !== 200) {
uni.showToast({
title: response.data.message,
duration: 2000,
icon: 'none',
})
return
}
/**
* 循环出当前月份的时间
* 例子:
* "","","","","","",1,
* 2 ,3 ,4 ,5 ,6 ,7 ,8,
* ...依次向下排
*/
getMonthDays(index, day) {
//day 当月1号是周几
this.dateArr = [];
this.dataObj = [];
for (var i = 0; i < day; i++) {
this.dateArr.push("");
}
if (
index == 0 ||
index == 2 ||
index == 4 ||
index == 6 ||
index == 7 ||
index == 9 ||
index == 11
) {
for (let i = 1; i < 32; i++) {
this.dateArr.push(i);
}
}
if (index == 3 || index == 5 || index == 8 || index == 10) {
for (let i = 1; i < 31; i++) {
this.dateArr.push(i);
}
}
if (index == 1) {
if (
(this.currentYear % 4 == 0 && this.currentYear % 100 != 0) ||
this.currentYear % 400 == 0
) {
for (let i = 1; i < 30; i++) {
this.dateArr.push(i);
}
} else {
for (let i = 1; i < 29; i++) {
this.dateArr.push(i);
}
}
}
for (var y = 0; y < 10; y++) {
if (this.dateArr.length > 7) {
this.dataObj.push(this.dateArr.splice(0, 7));
} else {
for (let i = 0; i < 7 - this.dateArr.length; i++) {
this.dateArr.push("");
}
}
}
this.dataObj.push(this.dateArr);
},
const animation = uni.createAnimation({ duration: 200, timingFunction: 'linear' })
signedDays.value.push(currentDay.value)
animation.rotateY(0).step()
animationData.value = animation.export()
/**
* 获取当前月份有几周
*/
getWeekByDay(dayValue) {
var day = new Date(Date.parse(dayValue.replace(/-/g, "/"))).getDay(); //将日期值格式化
return day;
},
},
};
setTimeout(() => {
signAnimating.value = true
showSuccessMask.value = true
hasSignedToday.value = true
animation.rotateY(0).step()
animationData.value = animation.export()
}, 200)
}
function closeSuccessMask() {
showSuccessMask.value = false
maskClosing.value = true
setTimeout(() => {
maskClosing.value = false
}, 500)
}
function buildCalendar() {
const date = new Date()
const monthIndex = date.getMonth()
currentYear.value = date.getFullYear()
currentMonth.value = monthLabels[monthIndex]
currentMonthIndex.value = monthIndex + 1
currentDay.value = date.getDate()
syncSignedDays()
const firstWeekDay = getWeekByDay(`${currentYear.value}-${monthIndex + 1}-1`)
buildMonthDays(monthIndex, firstWeekDay)
}
function syncSignedDays() {
const date = new Date()
const monthIndex = date.getMonth()
signedDays.value = []
hasSignedToday.value = false
signRecords.value.forEach((item) => {
const datePart = item.createTime.split(' ')[0]
const parts = datePart.split('-')
if (
Number(parts[0]) === currentYear.value &&
Number(parts[1]) === currentMonthIndex.value
) {
signedDays.value.push(Number(parts[2]))
}
if (
Number(parts[0]) === date.getFullYear() &&
Number(parts[1]) === monthIndex + 1 &&
Number(parts[2]) === date.getDate()
) {
hasSignedToday.value = true
}
})
}
function buildMonthDays(monthIndex: number, firstWeekDay: number) {
const daysInRow: any[] = []
const rows: any[][] = []
for (let i = 0; i < firstWeekDay; i++) {
daysInRow.push('')
}
const isLongMonth = [0, 2, 4, 6, 7, 9, 11].includes(monthIndex)
const isFebruary = monthIndex === 1
let totalDays = isLongMonth ? 31 : 30
if (isFebruary) {
const year = currentYear.value
const isLeap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
totalDays = isLeap ? 29 : 28
}
for (let day = 1; day <= totalDays; day++) {
daysInRow.push(day)
}
while (daysInRow.length) {
const row = daysInRow.splice(0, 7)
while (row.length < 7) {
row.push('')
}
rows.push(row)
}
calendarRows.value = rows
}
function getWeekByDay(dayValue: string) {
return new Date(Date.parse(dayValue.replace(/-/g, '/'))).getDay()
}
</script>
<style scoped>
page {

View File

@@ -185,6 +185,8 @@ page {
}
.goods-list-wrap {
width: 100%;
box-sizing: border-box;
padding-bottom: 40rpx;
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@
:scrollable="false"
v-model:current="current"
@change="change"
:lineColor="$lightColor"
:activeStyle="{ color: $lightColor }"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
></u-tabs>
</view>
<div class="u-tabs-search">
@@ -194,279 +194,199 @@
</view>
</template>
<script>
import uniLoadMore from "@/components/uni-load-more/uni-load-more.vue";
import { getAfterSaleList, cancelAfterSale } from "@/api/after-sale.js";
import { getOrderList } from "@/api/order.js";
import storage from "@/utils/storage";
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { onLoad, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice, serviceStatusList, parseGoodsImageUrl } from '@/utils/filters.js'
import { getAfterSaleList, cancelAfterSale } from '@/api/after-sale.js'
import { getOrderList } from '@/api/order.js'
import storage from '@/utils/storage'
export default {
components: {
uniLoadMore,
},
data() {
return {
list: [
//tab表头
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const list = [
{ name: '售后申请' },
{ name: '申请中' },
{ name: '申请记录' },
]
const current = ref(0)
const tipsShow = ref(false)
const cancelShow = ref(false)
const selectedOrder = ref<any>(null)
const orderList = ref<any[]>([])
const params = reactive<Record<string, any>>({
pageNumber: 1,
pageSize: 10,
sort: 'createTime',
flowPrice: 0,
order: 'desc',
})
const logParams = reactive<Record<string, any>>({
pageNumber: 1,
pageSize: 10,
})
const status = ref('loadmore')
const keywords = ref('')
onLoad((options) => {
orderList.value = []
params.pageNumber = 1
if (options?.orderSn) params.keywords = options.orderSn
searchOrderList(current.value)
})
onPullDownRefresh(() => {
change(current.value)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function getGoodsName(sku: any) {
return sku.goodsName || sku.name || ''
}
function getGoodsImage(goods: any, order: any, index: number) {
let image = goods.image || goods.goodsImage || goods.thumbnail
if (!image && order.groupImages) {
const images = String(order.groupImages).split(',')
image = images[index] || images[0]
}
return parseGoodsImageUrl(image)
}
function submitSearchOrderList(tabIndex: number) {
params.pageNumber = 1
logParams.pageNumber = 1
orderList.value = []
searchOrderList(tabIndex)
}
function clear(tabIndex: number) {
params.pageNumber = 1
logParams.pageNumber = 1
params.keywords = ''
orderList.value = []
searchOrderList(tabIndex)
}
function change(e: number | { index: number }) {
const index = typeof e === 'object' && e != null ? e.index : e
current.value = index
Object.assign(params, { pageNumber: 1, pageSize: 10 })
orderList.value = []
searchOrderList(index)
uni.stopPullDownRefresh()
}
function searchOrderList(index: number) {
if (index == 0) {
if (keywords.value) params.keywords = keywords.value
fetchOrderList()
} else {
Object.assign(logParams, {
pageNumber: 1,
pageSize: 10,
sort: 'createTime',
order: 'desc',
})
if (index === 1) {
logParams.serviceStatus = 'APPLY'
}
if (keywords.value) logParams.keywords = keywords.value
orderList.value = []
fetchAfterSaleLogList()
}
}
function fetchOrderList() {
uni.showLoading({ title: '加载中', mask: true })
getOrderList(params).then((res) => {
hideLoadingIfNeeded()
const records = res.data.result.records
if (records.length > 0) {
orderList.value = orderList.value.concat(records)
params.pageNumber += 1
}
status.value = records.length < 10 ? 'nomore' : 'loading'
})
}
function close(order: any, _sku: any) {
selectedOrder.value = order
cancelShow.value = true
}
async function closeService() {
uni.showLoading({ title: '加载中' })
const res = await cancelAfterSale(selectedOrder.value.sn)
if (res.data.success) {
uni.showToast({ title: '取消成功!', duration: 2000, icon: 'none' })
}
orderList.value = []
searchOrderList(current.value)
hideLoadingIfNeeded()
}
function afterDetails(order: any, _sku?: any) {
uni.navigateTo({ url: './applyDetail?sn=' + order.sn })
}
function fetchAfterSaleLogList() {
getAfterSaleList(logParams).then((res) => {
const afterSaleLogList = res.data.result.records
afterSaleLogList.forEach((item: any) => {
item.orderItems = [
{
name: "售后申请",
image: item.goodsImage,
skuId: item.skuId,
name: item.goodsName,
num: item.num,
price: item.flowPrice,
},
{
name: "申请中",
},
{
name: "申请记录",
},
],
current: 0, //当前表头索引
tipsShow: false, //提示开关
cancelShow: false, //取消显示开关
selectedOrder: "", //选中的order
orderList: [], //订单集合
params: {
pageNumber: 1,
pageSize: 10,
sort: "createTime",
flowPrice: 0,
order: "desc",
},
]
})
orderList.value = orderList.value.concat(afterSaleLogList)
status.value = afterSaleLogList.length < 10 ? 'nomore' : 'loading'
})
}
logParams: {
pageNumber: 1,
pageSize: 10,
},
status: "loadmore",
keywords: "", // 搜索订单sn
};
},
onLoad(options) {
this.orderList = [];
this.params.pageNumber = 1;
if (options.orderSn) this.params.keywords = options.orderSn;
this.searchOrderList(this.current);
},
onPullDownRefresh() {
this.change(this.current);
},
methods: {
getGoodsName(sku) {
return sku.goodsName || sku.name || "";
},
getGoodsImage(goods, order, index) {
let image = goods.image || goods.goodsImage || goods.thumbnail;
if (!image && order.groupImages) {
const images = String(order.groupImages).split(",");
image = images[index] || images[0];
}
return this.parseGoodsImageUrl(image);
},
/**
* 点击搜索执行搜索
*/
submitSearchOrderList(current) {
this.params.pageNumber = 1;
this.logParams.pageNumber = 1;
this.orderList = [];
this.searchOrderList(current);
},
// 清空
clear(current){
this.params.pageNumber = 1;
this.logParams.pageNumber = 1;
this.params.keywords = ''
this.orderList = [];
this.searchOrderList(current);
},
/**
* 切换tab页时初始化数据
*/
change(e) {
const index = typeof e === 'object' && e != null ? e.index : e;
this.current = index;
this.params = {
pageNumber: 1,
pageSize: 10,
};
this.orderList = [];
//如果是2 则读取售后申请记录列表
this.searchOrderList(index);
uni.stopPullDownRefresh();
},
function applyService(sn: string, order: any, sku: any) {
storage.setAfterSaleData({ ...order, ...sku })
uni.navigateTo({ url: `/pages/order/afterSales/afterSalesSelect?sn=${sn}` })
}
/**
* 搜索初始化
* 根据当前tab传值的索引进行更改
*/
searchOrderList(index) {
if (index == 0) {
this.keywords ? (this.params.keywords = this.keywords) : "";
this.getOrderList();
} else {
this.logParams = {
pageNumber: 1,
pageSize: 10,
sort: "createTime",
order: "desc",
};
if (index === 1) {
this.logParams.serviceStatus = "APPLY";
}
this.keywords ? (this.logParams.keywords = this.keywords) : "";
this.orderList = [];
this.getAfterSaleLogList();
}
},
function onExpress(order: any, sku: any) {
sku.storeName = order.storeName
storage.setAfterSaleData({ ...order, ...sku })
uni.navigateTo({ url: `./afterSalesDetailExpress?serviceSn=${order.sn}` })
}
/**
* 获取订单列表
*/
getOrderList() {
uni.showLoading({
title: "加载中",
mask: true,
});
getOrderList(this.params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
const orderList = res.data.result.records;
if (orderList.length > 0) {
this.orderList = this.orderList.concat(orderList);
this.params.pageNumber += 1;
}
if (orderList.length < 10) {
this.status = "nomore";
} else {
this.status = "loading";
}
});
},
function onDetail(goods: any, sku: any) {
if (current.value == 0) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId || sku.goodsId}`,
})
} else {
uni.navigateTo({
url: `/pages/product/goods?id=${goods.skuId}&goodsId=${goods.goodsId || goods.goodsId}`,
})
}
}
close(order, sku) {
console.log(order, sku);
this.selectedOrder = order;
this.cancelShow = true;
},
async closeService() {
uni.showLoading({
title: "加载中",
});
console.log(this.selectedOrder);
let res = await cancelAfterSale(this.selectedOrder.sn);
if (res.data.success) {
uni.showToast({
title: "取消成功!",
duration: 2000,
icon: "none",
});
}
this.orderList = [];
this.searchOrderList(this.current);
if (this.$store.state.isShowToast){ uni.hideLoading() };
},
/**
* 售后详情
*/
afterDetails(order) {
uni.navigateTo({
url: "./applyDetail?sn=" + order.sn,
});
},
/**
* 申请记录列表
*/
getAfterSaleLogList() {
getAfterSaleList(this.logParams).then((res) => {
let afterSaleLogList = res.data.result.records;
afterSaleLogList.forEach((item) => {
item.orderItems = [
{
image: item.goodsImage,
skuId: item.skuId,
name: item.goodsName,
num: item.num,
price: item.flowPrice,
},
];
});
this.orderList = this.orderList.concat(afterSaleLogList);
if (afterSaleLogList.length < 10) {
this.status = "nomore";
} else {
this.status = "loading";
}
});
},
/**
* 申请售后
*/
applyService(sn, order, sku) {
let data = {
...order,
...sku,
};
storage.setAfterSaleData(data);
uni.navigateTo({
url: `/pages/order/afterSales/afterSalesSelect?sn=${sn}`,
});
},
/**
* 提交物流信息
*/
onExpress(order, sku) {
sku.storeName = order.storeName;
let data = {
...order,
...sku,
};
storage.setAfterSaleData(data);
uni.navigateTo({
url: `./afterSalesDetailExpress?serviceSn=${order.sn}`,
});
},
/**
* 查看详情
*/
onDetail(goods, sku) {
// 售后申请
if (this.current == 0) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${
sku.goodsId || sku.goodsId
}`,
});
} else {
uni.navigateTo({
url: `/pages/product/goods?id=${goods.skuId}&goodsId=${
goods.goodsId || goods.goodsId
}`,
});
}
},
/**
* 底部加载数据
*/
renderDate() {
if (this.current === 0) {
this.params.pageNumber += 1;
this.getOrderList();
} else {
this.logParams.pageNumber += 1;
this.getAfterSaleLogList();
}
},
},
};
function renderDate() {
if (current.value === 0) {
params.pageNumber += 1
fetchOrderList()
} else {
logParams.pageNumber += 1
fetchAfterSaleLogList()
}
}
</script>
<style lang="scss">

View File

@@ -1,7 +1,12 @@
<template>
<view class="page-wrap">
<scroll-view scroll-y class="page-scroll">
<u-form :model="form" ref="uForm">
<up-form
:model="form"
ref="uForm"
label-position="left"
label-width="180rpx"
>
<view class="after-sales-goods-detail-view">
<view class="header">
<view>
@@ -58,7 +63,7 @@
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</view>
</view>
<u-form-item label="申请说明" label-width="150" :border-bottom="false" class="desc-item">
<up-form-item label="申请说明" label-width="180rpx" :border-bottom="false" class="desc-item">
<u-input
v-model="form.problemDesc"
type="textarea"
@@ -67,54 +72,54 @@
height="120"
placeholder="请描述申请售后的说明"
/>
</u-form-item>
</up-form-item>
</view>
<!-- 退款方式 / 银行信息 -->
<view class="opt-view form-block">
<u-form-item label="退款方式" label-width="150" :border-bottom="true">
<up-form-item label="退款方式" label-width="180rpx" :border-bottom="true">
<view class="form-value">{{
applyInfo.refundWay == 'ORIGINAL' ? '原路退回' : '账号退款'
}}</view>
</u-form-item>
</up-form-item>
<template v-if="
applyInfo.accountType === 'BANK_TRANSFER' &&
applyInfo.applyRefundPrice != 0
">
<u-form-item label="银行开户行" label-width="150" :border-bottom="true">
<up-form-item label="银行开户行" label-width="180rpx" :border-bottom="true">
<u-input
v-model="form.bankDepositName"
border="none"
input-align="right"
placeholder="请输入银行开户行"
/>
</u-form-item>
<u-form-item label="银行开户名" label-width="150" :border-bottom="true">
</up-form-item>
<up-form-item label="银行开户名" label-width="180rpx" :border-bottom="true">
<u-input
v-model="form.bankAccountName"
border="none"
input-align="right"
placeholder="请输入银行开户名"
/>
</u-form-item>
<u-form-item label="银行账号" label-width="150" :border-bottom="true" class="bank-account-item">
</up-form-item>
<up-form-item label="银行账号" label-width="180rpx" :border-bottom="true" class="bank-account-item">
<u-input
v-model="form.bankAccountNumber"
border="none"
input-align="right"
placeholder="请输入银行账号"
/>
</u-form-item>
</up-form-item>
</template>
<u-form-item
<up-form-item
v-if="form.serviceType !== 'RETURN_MONEY'"
label="返回方式"
label-width="150"
label-width="180rpx"
:border-bottom="false"
>
<view class="form-value">快递至第三方卖家</view>
</u-form-item>
</up-form-item>
</view>
<!-- 上传凭证 -->
@@ -133,7 +138,7 @@
<view class="opt-tip">提交服务单后,售后专员可能与您电话沟通,请保持手机畅通</view>
</view>
</u-form>
</up-form>
</scroll-view>
<view class="submit-view">
@@ -142,7 +147,7 @@
ripple
shape="circle"
v-if="applyInfo.refundWay"
:custom-style="{ backgroundColor: $lightColor, width: '100%' }"
:custom-style="{ backgroundColor: lightColor, width: '100%' }"
@click="onSubmit"
>提交申请</u-button>
</view>
@@ -158,246 +163,214 @@
</view>
</template>
<script>
import {
getAfterSaleReason,
applyReturn,
getAfterSaleInfo,
} from "@/api/after-sale";
<script setup lang="ts">
import { ref, reactive, computed, getCurrentInstance } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice, parseGoodsImageUrl } from '@/utils/filters.js'
import { getAfterSaleReason, applyReturn, getAfterSaleInfo } from '@/api/after-sale'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
import storage from '@/utils/storage.js'
import city from "@/components/m-city/m-city";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
import storage from "@/utils/storage.js";
export default {
component: {
city,
},
data() {
return {
storage,
list: [{ id: "", localName: "请选择", children: [] }],
fileList: [],
sn: "",
sku: {},
typeValue: 0,
value: "",
type: "textarea",
border: true,
//退款原因 弹出框
reasonSelectShow: false,
reasonList: [],
applyInfo: {},
form: {
orderItemSn: "", // 订单sn
skuId: "",
reason: "", //退款原因
problemDesc: "", //退款说明
images: [], //图片凭证
num: 1, //退货数量
goodsId: "", //商品id
accountType: "",
applyRefundPrice: "",
refundWay: "",
serviceType: "", //申请类型
},
};
},
const store = useStore()
const { proxy } = getCurrentInstance()!
const $u = proxy!.$u
/**
* 判断当前内容并生成数据
*/
onLoad(options) {
let navTitle = "申请售后";
this.form.serviceType = "RETURN_GOODS";
if (options.value == 1) {
navTitle = "申请退货";
this.form.serviceType = "RETURN_GOODS";
const lightColor = computed(() => store.getters.lightColor)
const fileList = ref<any[]>([])
const sn = ref('')
const sku = ref<any>({})
const reasonSelectShow = ref(false)
const reasonList = ref<any[]>([])
const applyInfo = ref<any>({})
const uToast = ref<any>(null)
const form = reactive({
orderItemSn: '',
skuId: '',
reason: '',
problemDesc: '',
images: [] as string[],
num: 1,
goodsId: '',
accountType: '',
applyRefundPrice: '',
refundWay: '',
serviceType: 'RETURN_GOODS',
bankDepositName: '',
bankAccountName: '',
bankAccountNumber: '',
})
onLoad((options) => {
let navTitle = '申请售后'
form.serviceType = 'RETURN_GOODS'
if (options?.value == '1') {
navTitle = '申请退货'
form.serviceType = 'RETURN_GOODS'
}
if (options?.value == '2') {
navTitle = '申请换货'
form.serviceType = 'EXCHANGE_GOODS'
}
if (options?.value == '3') {
navTitle = '申请退款'
form.serviceType = 'RETURN_MONEY'
}
uni.setNavigationBarTitle({ title: navTitle })
sn.value = options?.sn || ''
sku.value = storage.getAfterSaleData()
form.orderItemSn = options?.sn || ''
form.skuId = sku.value.skuId
form.num = sku.value.num
form.goodsId = sku.value.goodsId
fetchReasonActions(form.serviceType)
init(options?.sn || '')
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function getGoodsName(item: any) {
return item.goodsName || item.name || ''
}
function getGoodsImage(item: any) {
const image = item.image || item.goodsImage || item.thumbnail
return parseGoodsImageUrl(image)
}
function gotoGoodsDetail(goodsId: string) {
if (!goodsId) return
uni.navigateTo({
url: `/pages/product/goods?id=${form.skuId}&goodsId=${goodsId}`,
})
}
async function fetchReasonActions(serviceType: string) {
uni.showLoading({ title: '加载中' })
await getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
reasonList.value = res.data.result.map((item: any) => ({
value: item.id,
label: item.reason,
}))
}
if (options.value == 2) {
navTitle = "申请换货";
this.form.serviceType = "EXCHANGE_GOODS";
}
if (options.value == 3) {
navTitle = "申请退款";
this.form.serviceType = "RETURN_MONEY";
}
this.typeValue = options.value;
uni.setNavigationBarTitle({
title: navTitle, //此处写页面的title
});
this.sn = options.sn;
this.sku = storage.getAfterSaleData();;
})
hideLoadingIfNeeded()
}
this.form.orderItemSn = options.sn;
this.form.skuId = this.sku.skuId;
this.form.num = this.sku.num;
this.form.goodsId = this.sku.goodsId;
this.getReasonActions(this.form.serviceType);
this.init(options.sn);
},
methods: {
getGoodsName(item) {
return item.goodsName || item.name || "";
},
getGoodsImage(item) {
const image = item.image || item.goodsImage || item.thumbnail;
return this.parseGoodsImageUrl(image);
},
gotoGoodsDetail(goodsId) {
if (!goodsId) return;
uni.navigateTo({
url: `/pages/product/goods?id=${this.form.skuId}&goodsId=${goodsId}`,
});
},
/** 获取申请原因下拉框数据 */
async getReasonActions(serviceType) {
uni.showLoading({
title: "加载中",
});
await getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
let action = [];
res.data.result.forEach((item) => {
action.push({
value: item.id,
label: item.reason,
});
});
this.reasonList = action;
}
});
if (this.$store.state.isShowToast){ uni.hideLoading() };
},
//打开地区选择器
showCitySelect() {
this.$refs.cityPicker.show();
},
// 初始化数据
init(sn) {
getAfterSaleInfo(sn).then((response) => {
if (response.data.code == 400) {
uni.showToast({
title: response.data.message,
duration: 2000,
icon: "none",
});
} else {
this.applyInfo = response.data.result;
this.form.accountType = response.data.result.accountType;
}
});
},
openReasonPicker() {
if (!this.reasonList.length) {
uni.showToast({
title: "暂无可选原因",
icon: "none",
});
return;
}
this.reasonSelectShow = true;
},
//退款原因
reasonSelectConfirm(val) {
const selected = val?.value?.[0] || val?.[0];
if (selected) {
this.form.reason = selected.label || selected.text || "";
}
},
//修改申请数量
valChange(e) {
this.form.num = e.value;
},
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.fileList, (urls) => {
this.form.images = urls;
});
},
showToast(message, type = "error") {
const text = message || (type === "success" ? "操作成功" : "操作失败");
if (this.$refs.uToast) {
this.$refs.uToast.show({ message: text, type });
return;
}
function init(orderItemSn: string) {
getAfterSaleInfo(orderItemSn).then((response) => {
if (response.data.code == 400) {
uni.showToast({
title: text,
icon: type === "success" ? "success" : "none",
});
},
//提交申请
onSubmit() {
//提交申请前检测参数
if (!this.handleCheckParams()) {
return;
}
title: response.data.message,
duration: 2000,
icon: 'none',
})
} else {
applyInfo.value = response.data.result
form.accountType = response.data.result.accountType
}
})
}
uni.showLoading({
title: "加载中",
});
this.form.accountType = this.applyInfo.accountType;
this.form.refundWay = this.applyInfo.refundWay;
this.form.applyRefundPrice = this.applyInfo.applyRefundPrice;
function openReasonPicker() {
if (!reasonList.value.length) {
uni.showToast({ title: '暂无可选原因', icon: 'none' })
return
}
reasonSelectShow.value = true
}
applyReturn(this.sn, this.form).then((resp) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (resp.data.success) {
this.showToast("提交成功", "success");
uni.redirectTo({
url: "/pages/order/afterSales/applySuccess",
});
} else {
this.showToast(resp.data.message || "提交失败", "error");
}
});
},
//检测提交参数
handleCheckParams() {
if (this.$u.test.isEmpty(this.form.reason)) {
this.showToast("请选择退款原因");
return false;
}
if (this.$u.test.isEmpty(this.form.problemDesc)) {
this.showToast("请输入退款说明");
return false;
}
function reasonSelectConfirm(val: any) {
const selected = val?.value?.[0] || val?.[0]
if (selected) {
form.reason = selected.label || selected.text || ''
}
}
if (
this.applyInfo.accountType === "BANK_TRANSFER" &&
this.applyInfo.applyRefundPrice != 0
) {
if (this.$u.test.isEmpty(this.form.bankDepositName)) {
this.showToast("请输入银行开户行");
return false;
}
if (this.$u.test.isEmpty(this.form.bankAccountName)) {
this.showToast("请输入银行开户名");
return false;
}
if (this.$u.test.isEmpty(this.form.bankAccountNumber)) {
this.showToast("请输入银行账号");
return false;
}
if (this.$u.test.chinese(this.form.bankAccountName) === false) {
this.showToast("银行开户名需为中文");
return false;
}
if (this.$u.test.chinese(this.form.bankDepositName) === false) {
this.showToast("银行开户行需为中文");
return false;
}
}
function valChange(e: { value: number }) {
form.num = e.value
}
return true;
},
},
};
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, fileList.value, (urls) => {
form.images = urls
})
}
function showToast(message: string, type = 'error') {
const text = message || (type === 'success' ? '操作成功' : '操作失败')
if (uToast.value) {
uToast.value.show({ message: text, type })
return
}
uni.showToast({
title: text,
icon: type === 'success' ? 'success' : 'none',
})
}
function onSubmit() {
if (!validateFormParams()) return
uni.showLoading({ title: '加载中' })
form.accountType = applyInfo.value.accountType
form.refundWay = applyInfo.value.refundWay
form.applyRefundPrice = applyInfo.value.applyRefundPrice
applyReturn(sn.value, form).then((resp) => {
hideLoadingIfNeeded()
if (resp.data.success) {
showToast('提交成功', 'success')
uni.redirectTo({ url: '/pages/order/afterSales/applySuccess' })
} else {
showToast(resp.data.message || '提交失败', 'error')
}
})
}
function validateFormParams() {
if ($u.test.isEmpty(form.reason)) {
showToast('请选择退款原因')
return false
}
if ($u.test.isEmpty(form.problemDesc)) {
showToast('请输入退款说明')
return false
}
if (
applyInfo.value.accountType === 'BANK_TRANSFER' &&
applyInfo.value.applyRefundPrice != 0
) {
if ($u.test.isEmpty(form.bankDepositName)) {
showToast('请输入银行开户行')
return false
}
if ($u.test.isEmpty(form.bankAccountName)) {
showToast('请输入银行开户名')
return false
}
if ($u.test.isEmpty(form.bankAccountNumber)) {
showToast('请输入银行账号')
return false
}
if ($u.test.chinese(form.bankAccountName) === false) {
showToast('银行开户名需为中文')
return false
}
if ($u.test.chinese(form.bankDepositName) === false) {
showToast('银行开户行需为中文')
return false
}
}
return true
}
</script>
<style lang="scss" scoped>

View File

@@ -1,6 +1,11 @@
<template>
<view class="mp-iphonex-bottom content">
<u-form :model="form" ref="uForm">
<up-form
:model="form"
ref="uForm"
label-position="left"
label-width="180rpx"
>
<view class="after-sales-goods-detail-view">
<view class="header">
<view>
@@ -29,32 +34,32 @@
<!-- 上传凭证 -->
<view class="opt-view">
<view class="img-title" style="font-size: 30rpx">填写物流信息</view>
<u-form-item label="返回方式" :label-width="150">
<up-form-item label="返回方式" label-width="180rpx">
<div style="width: 100%; text-align: right;">快递至第三方卖家</div>
</u-form-item>
<u-form-item label="快递公司" :label-width="150">
</up-form-item>
<up-form-item label="快递公司" label-width="180rpx">
<div style="width: 100%; text-align: right;" @click="companySelectShow = true">
{{ form.courierCompany || '请选择快递公司' }}
</div>
</u-form-item>
<u-form-item label="快递单号" :label-width="150">
</up-form-item>
<up-form-item label="快递单号" label-width="180rpx">
<u-input input-align="right" v-model="form.logisticsNo" placeholder="请输入快递单号"/>
</u-form-item>
<u-form-item label="发货时间" :label-width="150">
</up-form-item>
<up-form-item label="发货时间" label-width="180rpx">
<div style="width: 100%; text-align: right;" @click="timeshow = true">{{
form.mDeliverTime || '请选择发货时间'
}}
</div>
</u-form-item>
</up-form-item>
</view>
</scroll-view>
<view class="submit-view">
<u-button ripple :customStyle="{'background':$lightColor,'color':'#fff' }" shape="circle" @click="onSubmit">
<u-button ripple :customStyle="{ background: lightColor, color: '#fff' }" shape="circle" @click="onSubmit">
提交申请
</u-button>
</view>
</u-form>
</up-form>
<u-select mode="single-column" :list="companyList" v-model:show="companySelectShow"
@confirm="companySelectConfirm"></u-select>
<u-calendar v-model:show="timeshow" :mode="'date'" @change="onTimeChange"></u-calendar>
@@ -62,124 +67,98 @@
</view>
</template>
<script>
import {getLogistics} from "@/api/address.js";
import {fillShipInfo} from "@/api/after-sale.js";
import storage from "@/utils/storage";
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { getLogistics } from '@/api/address.js'
import { fillShipInfo } from '@/api/after-sale.js'
import storage from '@/utils/storage'
export default {
data() {
return {
//快递公司 弹出框
companySelectShow: false,
companyList: [], //快递公司集合
timeshow: false, //发货时间
form: {
courierCompany: "", //快递公司
logisticsId: "", //快递公司ID
logisticsNo: "", //快递单号
mDeliverTime: "", //发货时间
},
serviceDetail: {}, //服务详情
sku: {}, //sku信息
};
},
onLoad(options) {
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
this.sku = storage.getAfterSaleData();
let navTitle = "服务单详情";
uni.setNavigationBarTitle({
title: navTitle, //此处写页面的title
});
this.serviceDetail.sn = options.serviceSn;
this.Logistics();
},
methods: {
/**
* 确认快递公司
*/
companySelectConfirm(e) {
this.form.logisticsId = e[0].value;
this.form.courierCompany = e[0].label;
},
const companySelectShow = ref(false)
const companyList = ref<any[]>([])
const timeshow = ref(false)
const form = reactive({
courierCompany: '',
logisticsId: '',
logisticsNo: '',
mDeliverTime: '',
})
const serviceDetail = reactive<{ sn: string }>({ sn: '' })
const sku = ref<any>({})
const uToast = ref<any>(null)
/**
* 获取快递公司
*/
Logistics() {
getLogistics().then((res) => {
if (res.data.success) {
res.data.result.forEach((item, index) => {
this.companyList[index] = {
value: item.id,
label: item.name,
};
});
}
});
},
onLoad((options) => {
sku.value = storage.getAfterSaleData()
uni.setNavigationBarTitle({ title: '服务单详情' })
serviceDetail.sn = options?.serviceSn || ''
fetchLogisticsList()
})
/**
* 更改时间
*/
onTimeChange(e) {
this.form.mDeliverTime = e.result;
},
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
/**
* 点击提交
*/
onSubmit() {
delete this.form.courierCompany;
function companySelectConfirm(e: any[]) {
form.logisticsId = e[0].value
form.courierCompany = e[0].label
}
if (this.form.logisticsId == "") {
this.$refs.uToast.show({
title: "请选择快递公司",
type: "error",
});
return;
}
if (this.form.logisticsNo == "") {
this.$refs.uToast.show({
title: "请填写快递单号",
type: "error",
});
return;
}
if (this.form.mDeliverTime == "") {
this.$refs.uToast.show({
title: "请选择发货时间",
type: "error",
});
return;
}
function fetchLogisticsList() {
getLogistics().then((res) => {
if (res.data.success) {
companyList.value = res.data.result.map((item: any) => ({
value: item.id,
label: item.name,
}))
}
})
}
uni.showLoading({
title: "加载中",
mask: true,
});
fillShipInfo(this.serviceDetail.sn, this.form).then((res) => {
if (this.$store.state.isShowToast) {
uni.hideLoading()
}
;
if (res.statusCode === 200) {
this.$refs.uToast.show({
title: "提交成功",
type: "success",
back: true,
url: "/pages/order/afterSales/afterSales",
});
}
});
},
gotoGoodsDetail(sku) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId}`,
});
},
},
};
function onTimeChange(e: { result: string }) {
form.mDeliverTime = e.result
}
function onSubmit() {
const submitForm = { ...form }
delete submitForm.courierCompany
if (form.logisticsId == '') {
uToast.value?.show({ title: '请选择快递公司', type: 'error' })
return
}
if (form.logisticsNo == '') {
uToast.value?.show({ title: '请填写快递单号', type: 'error' })
return
}
if (form.mDeliverTime == '') {
uToast.value?.show({ title: '请选择发货时间', type: 'error' })
return
}
uni.showLoading({ title: '加载中', mask: true })
fillShipInfo(serviceDetail.sn, submitForm).then((res) => {
hideLoadingIfNeeded()
if (res.statusCode === 200) {
uToast.value?.show({
title: '提交成功',
type: 'success',
back: true,
url: '/pages/order/afterSales/afterSales',
})
}
})
}
function gotoGoodsDetail(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.skuId}&goodsId=${item.goodsId}`,
})
}
</script>
<style lang="scss" scoped>

View File

@@ -57,59 +57,51 @@
</view>
</template>
<script>
import { getAfterSaleInfo } from "@/api/after-sale";
import storage from "@/utils/storage";
export default {
data() {
return {
sn: "",
sku: {}, //sku
applyInfo:""
};
},
onLoad(options) {
this.sn = options.sn;
this.sku = storage.getAfterSaleData();
// 查看当前商品是否支持退款退货
this.init()
},
methods: {
getGoodsName(item) {
return item.goodsName || item.name || "";
},
getGoodsImage(item) {
const image = item.image || item.goodsImage || item.thumbnail;
return this.parseGoodsImageUrl(image);
},
// 初始化数据
init() {
getAfterSaleInfo(this.sn).then((response) => {
if (response.data.success) {
this.applyInfo = response.data.result;
}
});
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { unitPrice, parseGoodsImageUrl } from '@/utils/filters.js'
import { getAfterSaleInfo } from '@/api/after-sale'
import storage from '@/utils/storage'
/**
* 选择退货流程
*/
onSelect(value) {
uni.redirectTo({
url: `./afterSalesDetail?sn=${this.sn}&value=${value}`,
});
},
const sn = ref('')
const sku = ref<any>({})
const applyInfo = ref<any>({})
/**
* 跳转到商品信息
*/
navigateToGoodsDetail(id) {
uni.navigateTo({
url: `/pages/product/goods?id=${id}&goodsId=${goodsId}`,
});
},
},
};
onLoad((options) => {
sn.value = options?.sn || ''
sku.value = storage.getAfterSaleData()
init()
})
function getGoodsName(item: any) {
return item.goodsName || item.name || ''
}
function getGoodsImage(item: any) {
const image = item.image || item.goodsImage || item.thumbnail
return parseGoodsImageUrl(image)
}
function init() {
getAfterSaleInfo(sn.value).then((response) => {
if (response.data.success) {
applyInfo.value = response.data.result
}
})
}
function onSelect(value: number) {
uni.redirectTo({
url: `./afterSalesDetail?sn=${sn.value}&value=${value}`,
})
}
function navigateToGoodsDetail(skuId: string) {
uni.navigateTo({
url: `/pages/product/goods?id=${skuId}&goodsId=${sku.value.goodsId}`,
})
}
</script>
<style lang="scss">

View File

@@ -25,7 +25,7 @@
</view>
<view class="goods-info">
<view class="info-box">
<view class="goods-item-view" @click="navgiateToGoodsDetail(serviceDetail)">
<view class="goods-item-view" @click="navigateToGoodsDetail(serviceDetail)">
<view class="goods-img">
<u-image
border-radius="6"
@@ -184,198 +184,184 @@
</view>
</template>
<script>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import {
unitPrice,
parseGoodsImageUrl,
serviceStatusList,
secrecyMobile,
unixToDate,
} from '@/utils/filters.js'
import {
getServiceDetail,
getStoreAfterSaleAddress,
getAfterSaleLog,
getAfterSaleReason,
} from "@/api/after-sale.js";
export default {
data() {
return {
reason: "", //申请原因
serviceTypeList: {
// 售后类型
CANCEL: "取消",
RETURN_GOODS: "退货",
EXCHANGE_GOODS: "换货",
RETURN_MONEY: "退款",
},
serviceDetail: {}, // 售后详情
logs: [], //日志
goodsList: [], //商品列表
storeAfterSaleAddress: {}, //售后地址
refundShow: false, //退款开关
accountShow: false, //账户显示
bankShow: false, //银行显示
sn: "", //订单sn
};
},
onLoad(options) {
uni.setNavigationBarTitle({
title: "服务单详情",
});
this.sn = options.sn;
this.loadDetail();
this.getAddress();
this.getLog(options.sn);
},
methods: {
statusFilter(val) {
switch (val) {
case "APPLY":
return "售后服务申请成功,等待商家审核";
case "PASS":
return "售后服务申请审核通过";
case "REFUSE":
return "售后服务申请已被商家拒绝,如有疑问请及时联系商家";
case "FULL_COURIER":
return "申请售后的商品已经寄出,等待商家收货";
case "STOCK_IN":
return "商家已将售后商品入库";
case "WAIT_FOR_MANUAL":
return "等待平台进行人工退款";
case "REFUNDING":
return "商家退款中,请您耐心等待";
case "COMPLETED":
return "售后服务已完成,感谢您的支持";
case "ERROR_EXCEPTION":
return "系统生成新订单异常,等待商家手动创建新订单";
case "CLOSED":
return "售后服务已关闭";
case "WAIT_REFUND":
return "等待平台进行退款";
default:
return "";
}
},
refundWayFilter(val) {
switch (val) {
case "OFFLINE":
return "账户退款";
case "ORIGINAL":
return "原路退回";
default:
return "";
}
},
accountTypeFilter(val) {
switch (val) {
case "WEIXINPAY":
return "微信";
case "ALIPAY":
return "支付宝";
case "BANK_TRANSFER":
return "银行卡";
default:
return "";
}
},
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
} from '@/api/after-sale.js'
/**
* 获取地址信息
*/
getAddress() {
getStoreAfterSaleAddress(this.sn).then((res) => {
if (res.data.success) {
this.storeAfterSaleAddress = res.data.result;
}
});
const store = useStore()
const reason = ref('')
const serviceTypeList: Record<string, string> = {
CANCEL: '取消',
RETURN_GOODS: '退货',
EXCHANGE_GOODS: '换货',
RETURN_MONEY: '退款',
}
const serviceDetail = ref<any>(null)
const logs = ref<any[]>([])
const storeAfterSaleAddress = ref<any>({})
const refundShow = ref(false)
const accountShow = ref(false)
const bankShow = ref(false)
const sn = ref('')
onLoad((options) => {
uni.setNavigationBarTitle({ title: '服务单详情' })
sn.value = options?.sn || ''
loadDetail()
fetchAddress()
fetchLog(sn.value)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function statusFilter(val: string) {
switch (val) {
case 'APPLY':
return '售后服务申请成功,等待商家审核'
case 'PASS':
return '售后服务申请审核通过'
case 'REFUSE':
return '售后服务申请已被商家拒绝,如有疑问请及时联系商家'
case 'FULL_COURIER':
return '申请售后的商品已经寄出,等待商家收货'
case 'STOCK_IN':
return '商家已将售后商品入库'
case 'WAIT_FOR_MANUAL':
return '等待平台进行人工退款'
case 'REFUNDING':
return '商家退款中,请您耐心等待'
case 'COMPLETED':
return '售后服务已完成,感谢您的支持'
case 'ERROR_EXCEPTION':
return '系统生成新订单异常,等待商家手动创建新订单'
case 'CLOSED':
return '售后服务已关闭'
case 'WAIT_REFUND':
return '等待平台进行退款'
default:
return ''
}
}
function refundWayFilter(val: string) {
switch (val) {
case 'OFFLINE':
return '账户退款'
case 'ORIGINAL':
return '原路退回'
default:
return ''
}
}
function accountTypeFilter(val: string) {
switch (val) {
case 'WEIXINPAY':
return '微信'
case 'ALIPAY':
return '支付宝'
case 'BANK_TRANSFER':
return '银行卡'
default:
return ''
}
}
function preview(urls: string[], index: number) {
uni.previewImage({
current: index,
urls,
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
})
}
/**
* 获取日志
*/
getLog(sn) {
getAfterSaleLog(sn).then((res) => {
this.logs = res.data.result;
});
},
function fetchAddress() {
getStoreAfterSaleAddress(sn.value).then((res) => {
if (res.data.success) {
storeAfterSaleAddress.value = res.data.result
}
})
}
/**
* 获取申请原因
*/
getReasonList(serviceType) {
getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
// 1357583466371219456
this.reason = this.serviceDetail.reason;
}
});
},
function fetchLog(serviceSn: string) {
getAfterSaleLog(serviceSn).then((res) => {
logs.value = res.data.result
})
}
/**
* 初始化详情
*/
loadDetail() {
uni.showLoading({
title: "加载中",
});
getServiceDetail(this.sn).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
this.serviceDetail = res.data.result;
if (
this.serviceDetail.serviceType == "RETURN_GOODS" ||
this.serviceDetail.serviceType === "RETURN_MONEY"
) {
this.refundShow = true;
}
function fetchReasonList(serviceType: string) {
getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
reason.value = serviceDetail.value.reason
}
})
}
this.accountShow =
(this.serviceDetail.serviceType === "RETURN_GOODS" ||
this.serviceDetail.serviceType === "ORDER_CANCEL") &&
this.serviceDetail.refundWay === "OFFLINE";
function loadDetail() {
uni.showLoading({ title: '加载中' })
getServiceDetail(sn.value).then((res) => {
hideLoadingIfNeeded()
serviceDetail.value = res.data.result
if (
serviceDetail.value.serviceType == 'RETURN_GOODS' ||
serviceDetail.value.serviceType === 'RETURN_MONEY'
) {
refundShow.value = true
}
this.bankShow =
this.serviceDetail.accountType === "BANK_TRANSFER" &&
this.serviceDetail.refundWay === "OFFLINE" &&
((this.serviceDetail.serviceType === "RETURN_GOODS") |
(this.serviceDetail.serviceType === "ORDER_CANCEL") ||
this.serviceDetail.serviceType === "RETURN_MONEY");
accountShow.value =
(serviceDetail.value.serviceType === 'RETURN_GOODS' ||
serviceDetail.value.serviceType === 'ORDER_CANCEL') &&
serviceDetail.value.refundWay === 'OFFLINE'
this.getReasonList(this.serviceDetail.serviceType);
});
},
bankShow.value =
serviceDetail.value.accountType === 'BANK_TRANSFER' &&
serviceDetail.value.refundWay === 'OFFLINE' &&
((serviceDetail.value.serviceType === 'RETURN_GOODS') |
(serviceDetail.value.serviceType === 'ORDER_CANCEL') ||
serviceDetail.value.serviceType === 'RETURN_MONEY')
/**
* 访问商品详情
*/
navgiateToGoodsDetail(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
fetchReasonList(serviceDetail.value.serviceType)
})
}
/**
* 进度
*/
onProgress() {
uni.navigateTo({
url: `./applyProgress?sn=${
this.serviceDetail.sn
}&createTime=${encodeURIComponent(this.serviceDetail.createTime)}
&logs=${encodeURIComponent(JSON.stringify(this.logs))}&serviceStatus=${
this.serviceDetail.serviceStatus
}`,
});
},
},
};
function navigateToGoodsDetail(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
function onProgress() {
uni.navigateTo({
url: `./applyProgress?sn=${
serviceDetail.value.sn
}&createTime=${encodeURIComponent(serviceDetail.value.createTime)}
&logs=${encodeURIComponent(JSON.stringify(logs.value))}&serviceStatus=${
serviceDetail.value.serviceStatus
}`,
})
}
</script>
<style lang="scss" scoped>

View File

@@ -35,54 +35,50 @@
</view>
</template>
<script>
export default {
data() {
return {
sn: "", //sn
createTime: "", //创建时间
logList: [], //日志集合
serviceStatus: "", //订单状态
};
},
onLoad(options) {
this.sn = options.sn;
this.createTime = decodeURIComponent(options.createTime);
this.serviceStatus = this.statusFilter(options.serviceStatus);
this.logList = JSON.parse(decodeURIComponent(options.logs));
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
},
methods: {
statusFilter(val) {
switch (val) {
case "APPLY":
return "售后服务申请成功,等待商家审核";
case "PASS":
return "售后服务申请审核通过";
case "REFUSE":
return "售后服务申请已被商家拒绝,如有疑问请及时联系商家";
case "FULL_COURIER":
return "申请售后的商品已经寄出,等待商家收货";
case "STOCK_IN":
return "商家已将售后商品入库";
case "WAIT_FOR_MANUAL":
return "等待平台进行人工退款";
case "REFUNDING":
return "商家退款中,请您耐心等待";
case "COMPLETED":
return "售后服务已完成,感谢您的支持";
case "ERROR_EXCEPTION":
return "系统生成新订单异常,等待商家手动创建新订单";
case "CLOSED":
return "售后服务已关闭";
case "WAIT_REFUND":
return "等待平台进行退款";
default:
return "";
}
},
},
};
const sn = ref('')
const createTime = ref('')
const logList = ref<any[]>([])
const serviceStatus = ref('')
function statusFilter(val: string) {
switch (val) {
case 'APPLY':
return '售后服务申请成功,等待商家审核'
case 'PASS':
return '售后服务申请审核通过'
case 'REFUSE':
return '售后服务申请已被商家拒绝,如有疑问请及时联系商家'
case 'FULL_COURIER':
return '申请售后的商品已经寄出,等待商家收货'
case 'STOCK_IN':
return '商家已将售后商品入库'
case 'WAIT_FOR_MANUAL':
return '等待平台进行人工退款'
case 'REFUNDING':
return '商家退款中,请您耐心等待'
case 'COMPLETED':
return '售后服务已完成,感谢您的支持'
case 'ERROR_EXCEPTION':
return '系统生成新订单异常,等待商家手动创建新订单'
case 'CLOSED':
return '售后服务已关闭'
case 'WAIT_REFUND':
return '等待平台进行退款'
default:
return ''
}
}
onLoad((options) => {
sn.value = options.sn || ''
createTime.value = decodeURIComponent(options.createTime || '')
serviceStatus.value = statusFilter(options.serviceStatus || '')
logList.value = JSON.parse(decodeURIComponent(options.logs || '[]'))
})
</script>
<style lang="scss" scoped>

View File

@@ -21,31 +21,18 @@
</view>
</template>
<script>
export default {
data() {
return {};
},
methods: {
/**
* 跳转到售后服务
*/
navigateToAfterSales() {
uni.redirectTo({
url: "/pages/order/afterSales/afterSales",
});
},
<script setup lang="ts">
function navigateToAfterSales() {
uni.redirectTo({
url: '/pages/order/afterSales/afterSales',
})
}
/**
* 跳转到首页
*/
navigateToHome() {
uni.switchTab({
url: "/pages/tabbar/home/index",
});
},
},
};
function navigateToHome() {
uni.switchTab({
url: '/pages/tabbar/home/index',
})
}
</script>
<style lang="scss" scoped>

View File

@@ -57,137 +57,112 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { getOrderDetail } from "@/api/order.js";
import { getComplainReason, addComplain } from "@/api/after-sale.js";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
export default {
data() {
return {
storage,
uploadFileList: [],
orderStatusMap: {
//订单状态列表
UNDELIVERED: "待发货",
PARTS_DELIVERED: "部分发货",
UNPAID: "未付款",
PAID: "已付款",
DELIVERED: "已发货",
CANCELLED: "已取消",
COMPLETE: "已完成",
TAKE: "已完成",
},
complainValue: "", //投诉内容
complainShow: false, //投诉主题开关
complainTopic: "", //投诉抱怨话题
complainList: [], // 投诉列表
images: [], //投诉内容图片
order: "", //订单
orderGoodsList: "", //订单商品
orderDetail: "", //订单详情
sn: "",
skuId: "", //商品skuid
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getOrderDetail } from '@/api/order.js'
import { getComplainReason, addComplain } from '@/api/after-sale.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
import { unitPrice } from '@/utils/filters.js'
onLoad(option) {
this.loadData(option.sn);
this.sn = option.sn;
this.skuId = option.skuId;
this.getReasion();
},
const store = useStore()
methods: {
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.uploadFileList, (urls) => {
this.images = urls;
});
},
/**
* 提交
*/
handleSubmit() {
if(!this.images.length && !this.complainValue){
uni.showToast({
title:'请上传图片凭证和投诉内容',
icon:'none'
const orderStatusMap: Record<string, string> = {
UNDELIVERED: '待发货',
PARTS_DELIVERED: '部分发货',
UNPAID: '未付款',
PAID: '已付款',
DELIVERED: '已发货',
CANCELLED: '已取消',
COMPLETE: '已完成',
TAKE: '已完成',
}
const uploadFileList = ref<any[]>([])
const complainValue = ref('')
const complainShow = ref(false)
const complainTopic = ref('')
const complainList = ref<any[]>([])
const images = ref<string[]>([])
const order = ref<Record<string, any>>({})
const orderGoodsList = ref<any[]>([])
const sn = ref('')
const skuId = ref('')
onLoad((option) => {
loadData(option.sn)
sn.value = option.sn
skuId.value = option.skuId
getReasion()
})
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, uploadFileList.value, (urls) => {
images.value = urls
})
}
function handleSubmit() {
if (!images.value.length && !complainValue.value) {
uni.showToast({
title: '请上传图片凭证和投诉内容',
icon: 'none',
})
return
}
const goods = orderGoodsList.value.filter((item) => item.skuId == skuId.value)
const data = {
complainTopic: complainTopic.value,
content: complainValue.value,
goodsId: goods[0].goodsId,
images: images.value,
orderSn: sn.value,
skuId: skuId.value,
}
addComplain(data).then((res) => {
if (res.data.success) {
uni.showToast({
title: '提交成功!',
duration: 2000,
icon: 'none',
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/order/complain/complainList',
})
return
}, 1000)
}
})
}
}
// 循环出商品
let goods = this.orderGoodsList.filter((item) => {
return item.skuId == this.skuId;
});
//数据赋值
let data = {
complainTopic: this.complainTopic, //投诉主题,
content: this.complainValue, //投诉内容
goodsId: goods[0].goodsId, //商品id
images: this.images, //图片
orderSn: this.sn, //订单号
skuId: this.skuId, //skuid
};
addComplain(data).then((res) => {
if (res.data.success) {
uni.showToast({
title: "提交成功!",
duration: 2000,
icon: "none",
});
function getReasion() {
getComplainReason().then((res) => {
if (res.data.result.length >= 1) {
res.data.result.forEach((item: any) => {
complainList.value.push({
value: item.reason,
label: item.reason,
})
})
complainTopic.value = res.data.result[0].reason
}
})
}
setTimeout(() => {
uni.redirectTo({
url: "/pages/order/complain/complainList",
});
}, 1000);
}
});
},
function loadData(orderSn: string) {
uni.showLoading({ title: '加载中' })
getOrderDetail(orderSn).then((res) => {
const result = res.data.result
order.value = result.order
orderGoodsList.value = result.orderItems
if (store.state.isShowToast) uni.hideLoading()
})
}
/**
* 获取投诉原因
*/
getReasion() {
getComplainReason().then((res) => {
if (res.data.result.length >= 1) {
res.data.result.forEach((item) => {
let way = {
value: item.reason,
label: item.reason,
};
this.complainList.push(way);
});
this.complainTopic = res.data.result[0].reason;
}
});
},
/**
* 加载订单详情
*/
loadData(sn) {
uni.showLoading({
title: "加载中",
});
getOrderDetail(sn).then((res) => {
const order = res.data.result;
this.order = order.order;
this.orderGoodsList = order.orderItems;
this.orderDetail = res.data.result;
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
/**
* 确认投诉
*/
confirmComplain(e) {
this.complainTopic = e[0].label;
},
},
};
function confirmComplain(e: any[]) {
complainTopic.value = e[0].label
}
</script>
<style lang="scss" scoped>

View File

@@ -34,7 +34,7 @@
<span>{{ complaint.content }}</span>
</view>
</view>
<view class="speak-way" v-else>暂无对话</view>
<view class="speak-way" v-else>暂无对话</view>
<div v-if="complainDetail.complainStatus!='COMPLETE'">
<view class="tips">回复对话</view>
<view class="cell-item complain-content">
@@ -42,7 +42,7 @@
<u-input type="textarea" height="70rpx" auto-height v-model="complainValue" />
</view>
</view>
<view class="submit-btn" @click="handleSubmit">回复</view>
<view class="submit-btn" @click="handleSubmit">回复</view>
</div>
<view class="tips">平台仲裁</view>
<u-cell-group>
@@ -51,99 +51,91 @@
</view>
</template>
<script>
import { getComplainDetail, communication } from "@/api/after-sale";
export default {
data() {
return {
complainId: "",
complainValue: "", //回复内容
complainDetail: "", //投诉详情
statusData: {
NEW: "新投诉",
NO_APPLY: "未申请",
APPLYING: "申请中",
COMPLETE: "已完成",
EXPIRED: "已失效",
CANCEL: "已取消",
WAIT_ARBITRATION:"等待仲裁"
},
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getComplainDetail, communication } from '@/api/after-sale'
onLoad(option) {
this.complainId = option.id;
this.init(option.id);
},
methods: {
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
handleSubmit() {
if (!this.complainValue) {
uni.showToast({
title: "请输入回复内容",
duration: 2000,
icon: "none",
});
return;
}
let params = {
content: this.complainValue,
complainId: this.complainId,
};
communication(params).then((res) => {
if (res.data.success) {
uni.showToast({
title: "回复成功",
duration: 2000,
icon: "none",
});
this.complainValue = '';
this.init(this.complainId);
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
const store = useStore()
const complainId = ref('')
const complainValue = ref('')
const complainDetail = ref<Record<string, any>>({})
const statusData: Record<string, string> = {
NEW: '新投诉',
NO_APPLY: '未申请',
APPLYING: '申请中',
COMPLETE: '已完成',
EXPIRED: '已失效',
CANCEL: '已取消',
WAIT_ARBITRATION: '等待仲裁',
}
onLoad((option) => {
complainId.value = option.id
init(option.id)
})
function preview(urls: string[], index: number) {
uni.previewImage({
current: index,
urls,
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
/**
* 初始化投诉详情
*/
init(id) {
uni.showLoading({
title: "加载中",
});
getComplainDetail(id).then((res) => {
if (res.data.success) {
this.complainDetail = res.data.result;
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
})
}
function handleSubmit() {
if (!complainValue.value) {
uni.showToast({
title: '请输入回复内容',
duration: 2000,
icon: 'none',
})
return
}
const params = {
content: complainValue.value,
complainId: complainId.value,
}
communication(params).then((res) => {
if (res.data.success) {
uni.showToast({
title: '回复成功',
duration: 2000,
icon: 'none',
})
complainValue.value = ''
init(complainId.value)
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
function init(id: string) {
uni.showLoading({ title: '加载中' })
getComplainDetail(id).then((res) => {
if (res.data.success) {
complainDetail.value = res.data.result
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
if (store.state.isShowToast) uni.hideLoading()
})
}
</script>
<style lang="scss" scoped>
.row {

View File

@@ -1,181 +1,239 @@
<template>
<view>
<view class="seller-view" v-for="(item, index) in complaionData" :key="index">
<view class="seller-info u-flex u-row-between">
<view class="seller-name">
<view class="name">{{ item.storeName }}</view>
</view>
<view class="order-sn">{{ statusData[item.complainStatus] }}</view>
</view>
<u-line color="#DCDFE6"></u-line>
<view class="goods-item-view">
<view class="goods-img" @click="handleToGoods(item)">
<u-image border-radius="6" width="131rpx" height="131rpx" :src="item.goodsImage"></u-image>
</view>
<view class="goods-info" @click="handleToGoods(item)">
<view class="goods-title u-line-2">{{ item.goodsName }}</view>
<view class="goods-price">
{{unitPrice(item.goodsPrice) }}
<!-- <span>+{{ '1' }}积分</span> -->
</view>
</view>
<view class="goods-num">
<view>x{{ item.num }}</view>
</view>
</view>
<view class="complain-item-view">
<view class="complain-time"> {{ item.createTime }} </view>
<view class="complain-speak"> {{ item.complainTopic }} </view>
</view>
<view class="complain-btn">
<u-tag mode="plain" @click="handleClear(item)" class="complain-tag" text="撤销投诉" type="info"
v-if="item.complainStatus === 'APPLYING' || item.complainStatus === 'NEW'" />
<u-tag mode="plain" @click="handleInfo(item)" class="complain-tag" text="投诉详情" type="info" />
</view>
</view>
<u-empty v-if="empty" :style="{'marginTop':complaionDetail.total == 0 ? '200rpx':'0rpx'}" class="empty" style="" text="暂无投诉列表" mode="list"></u-empty>
<u-modal show-cancel-button @confirm="handleClearConfirm" v-model:show="show" :content="content"></u-modal>
</view>
</template>
<script>
import { getComplain, clearComplain } from "@/api/after-sale";
export default {
data() {
return {
statusData: {
NEW: "新投诉",
NO_APPLY: "未申请",
APPLYING: "申请中",
COMPLETE: "已完成",
EXPIRED: "已失效",
CANCEL: "已取消",
WAIT_ARBITRATION:"等待仲裁"
},
show: false,
content: "是否撤销投诉?",
params: {
pageNumber: 1,
pageSize: 20,
},
complaionDetail: "", //返回的整个response
complaionData: [], //投诉列表
empty: false,
checkComplainData: "", //存储投诉信息
};
},
mounted() {
this.init();
},
/**
* 触底加载
*/
onReachBottom() {
if (
this.complaionDetail &&
this.complaionDetail.total < this.params.pageNumber * this.params.pageSize
) {
this.params.pageNumber++;
this.init();
}
},
methods: {
// 点击跳转到商品
handleToGoods(val) {
uni.navigateTo({
url: "/pages/product/goods?id=" + val.skuId + "&goodsId=" + val.goodsId,
});
},
/**
* 点击撤销投诉
*/
handleClear(val) {
this.show = true;
this.checkComplainData = val;
},
/**
* 执行撤销
*/
handleClearConfirm() {
clearComplain(this.checkComplainData.id).then((res) => {
if (res.data.success) {
uni.showToast({
title: "撤销成功",
duration: 2000,
icon: "none",
});
this.complaionData = [];
this.params.pageNumber = 1;
this.init();
}
});
},
/**
* 查看详情
*/
handleInfo(val) {
uni.navigateTo({
url: "./complainInfo?id=" + val.id,
});
},
/**
* 初始化投诉列表
*/
init() {
uni.showLoading({
title: "加载中",
});
getComplain(this.params).then((res) => {
this.complaionDetail = res.data.result;
if (res.data.result.records.length >= 1) {
this.complaionData.push(...res.data.result.records);
} else {
this.empty = true;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
</script>
<style lang="scss" scoped>
@import "../goods.scss";
.complain-item-view {
border-bottom: 2rpx solid #f5f7fa;
border-top: 2rpx solid #f5f7fa;
padding: 20rpx 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.complain-time {
font-size: 24rpx;
color: #999;
}
::v-deep .seller-name {
width: auto !important;
}
.complain-btn {
padding: 20rpx 0;
display: flex;
align-items: center;
justify-content: flex-end;
margin-right: 30rpx;
}
.complain-tag {
margin-left: 10rpx;
}
.empty {
margin-top: 40rpx;
}
</style>
<template>
<view>
<view class="seller-view" v-for="(item, index) in complaionData" :key="index">
<view class="seller-info u-flex u-row-between">
<view class="seller-name">
<view class="name">{{ item.storeName }}</view>
</view>
<view class="order-sn">{{ statusData[item.complainStatus] }}</view>
</view>
<u-line color="#DCDFE6"></u-line>
<view class="goods-item-view">
<view class="goods-img" @click="handleToGoods(item)">
<u-image radius="6rpx" width="131rpx" height="131rpx" :src="item.goodsImage"></u-image>
</view>
<view class="goods-info" @click="handleToGoods(item)">
<view class="goods-title u-line-2">{{ item.goodsName }}</view>
<view class="goods-price">
{{unitPrice(item.goodsPrice) }}
<!-- <span>+{{ '1' }}积分</span> -->
</view>
</view>
<view class="goods-num">
<view>x{{ item.num }}</view>
</view>
</view>
<view class="complain-item-view">
<view class="complain-time"> {{ item.createTime }} </view>
<view class="complain-speak"> {{ item.complainTopic }} </view>
</view>
<view class="complain-btn">
<u-tag plain @click="handleClear(item)" class="complain-tag" text="撤销投诉" type="info"
v-if="item.complainStatus === 'APPLYING' || item.complainStatus === 'NEW'" />
<u-tag plain @click="handleInfo(item)" class="complain-tag" text="投诉详情" type="info" />
</view>
</view>
<u-empty v-if="empty" :style="{'marginTop':complaionDetail.total == 0 ? '200rpx':'0rpx'}" class="empty" style="" text="暂无投诉列表" mode="list"></u-empty>
<u-modal show-cancel-button @confirm="handleClearConfirm" v-model:show="show" :content="content"></u-modal>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { getComplain, clearComplain } from '@/api/after-sale'
const store = useStore()
const statusData: Record<string, string> = {
NEW: '新投诉',
NO_APPLY: '未申请',
APPLYING: '申请中',
COMPLETE: '已完成',
EXPIRED: '已失效',
CANCEL: '已取消',
WAIT_ARBITRATION: '等待仲裁',
}
const show = ref(false)
const content = ref('是否撤销投诉?')
const params = ref({ pageNumber: 1, pageSize: 20 })
const complaionDetail = ref<any>(null)
const complaionData = ref<any[]>([])
const empty = ref(false)
const checkComplainData = ref<any>(null)
onLoad(() => {
init()
})
onReachBottom(() => {
if (
complaionDetail.value &&
complaionDetail.value.total > params.value.pageNumber * params.value.pageSize
) {
params.value.pageNumber++
init()
}
})
function handleToGoods(val: any) {
uni.navigateTo({
url: '/pages/product/goods?id=' + val.skuId + '&goodsId=' + val.goodsId,
})
}
function handleClear(val: any) {
show.value = true
checkComplainData.value = val
}
function handleClearConfirm() {
clearComplain(checkComplainData.value.id).then((res) => {
if (res.data.success) {
uni.showToast({
title: '撤销成功',
duration: 2000,
icon: 'none',
})
complaionData.value = []
params.value.pageNumber = 1
init()
}
})
}
function handleInfo(val: any) {
uni.navigateTo({
url: './complainInfo?id=' + val.id,
})
}
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function init() {
uni.showLoading({
title: '加载中',
})
getComplain(params.value).then((res) => {
complaionDetail.value = res.data.result
if (res.data.result.records.length >= 1) {
complaionData.value.push(...res.data.result.records)
} else {
empty.value = true
}
hideLoadingIfNeeded()
})
}
</script>
<style lang="scss" scoped>
.seller-view {
background-color: #fff;
margin: 20rpx 0;
}
.seller-info {
height: 70rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.seller-name {
width: auto;
min-width: 0;
font-size: 33rpx;
font-weight: 600;
display: flex;
flex-direction: row;
align-items: center;
height: 90rpx;
}
.name {
margin-left: 15rpx;
margin-top: -2rpx;
font-size: 28rpx;
flex: 1;
}
.order-sn {
color: #ff0000;
font-size: 26rpx;
flex-shrink: 0;
margin-left: 20rpx;
}
.goods-item-view {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10rpx 30rpx;
}
.goods-img {
width: 131rpx;
height: 131rpx;
flex: 0 0 131rpx;
}
.goods-info {
padding-left: 30rpx;
min-width: 0;
flex: 1;
}
.goods-title {
margin-bottom: 10rpx;
color: $font-color-dark;
}
.goods-price {
font-size: 28rpx;
margin-bottom: 10rpx;
color: #ff5a10;
}
.goods-num {
text-align: center;
flex: 0 0 60rpx;
width: 60rpx;
color: $main-color;
}
.complain-item-view {
border-bottom: 2rpx solid #f5f7fa;
border-top: 2rpx solid #f5f7fa;
padding: 20rpx 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.complain-time {
font-size: 24rpx;
color: #999;
}
.complain-btn {
padding: 20rpx 0;
display: flex;
align-items: center;
justify-content: flex-end;
margin-right: 30rpx;
}
.complain-tag {
margin-left: 10rpx;
}
.empty {
margin-top: 40rpx;
}
</style>

View File

@@ -45,34 +45,24 @@
</div>
</template>
<script>
import { getPackage } from "@/api/trade.js";
export default {
data() {
return {
order: {},
logisticsList: [],
}
},
components: {
},
computed: {
},
onLoad(option) {
let sn = option.order_sn;
this.tracesList(sn);
},
mounted() {
},
methods: {
tracesList(sn) {
getPackage(sn).then((res) => {
if(res.data.success){
this.logisticsList = res.data.result;
}
});
},
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getPackage } from '@/api/trade.js'
const logisticsList = ref<any[]>([])
onLoad((option) => {
const sn = option.order_sn
if (sn) fetchLogistics(sn)
})
function fetchLogistics(sn: string) {
getPackage(sn).then((res) => {
if (res.data.success) {
logisticsList.value = res.data.result
}
})
}
</script>
<style >

View File

@@ -31,7 +31,7 @@
<view class="img">
<!-- 循环出商家回复评价的图片 -->
<u-image width="140rpx" height="140rpx" v-if="comment.replyImage" v-for="(replyImg, replyIndex) in splitImg(comment.replyImage)" :src="replyImg" :key="replyIndex"
@click="preview(splitImg( comment.replyImage), index)">
@click="preview(splitImg(comment.replyImage), replyIndex)">
</u-image>
</view>
</view>
@@ -40,56 +40,46 @@
</view>
</template>
<script>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import configs from '@/config/config'
export default {
data() {
return {
configs,
userImage:configs.defaultUserPhoto,
comment: {}, //评论信息
gradeList: {
//评价grade
GOOD: "好评",
MODERATE: "中评",
WORSE: "差评",
haveImage: "有图",
},
};
},
onLoad(options) {
this.comment = JSON.parse(decodeURIComponent(options.comment));
},
methods: {
/**
* 切割图像
*/
splitImg(val) {
if (val && val.split(",")) {
return val.split(",");
} else if (val) {
return val;
} else {
return false;
}
const userImage = configs.defaultUserPhoto
const comment = ref<Record<string, any>>({})
const gradeList: Record<string, string> = {
GOOD: '好评',
MODERATE: '中评',
WORSE: '差评',
haveImage: '有图',
}
onLoad((options) => {
comment.value = JSON.parse(decodeURIComponent(options.comment))
})
function splitImg(val: string) {
if (val && val.split(',')) {
return val.split(',')
} else if (val) {
return val
}
return false
}
function preview(urls: string[] | false, index: number) {
if (!urls) return
uni.previewImage({
current: index,
urls: Array.isArray(urls) ? urls : [urls],
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
},
};
})
}
</script>
<style lang="scss" scoped>

View File

@@ -8,8 +8,9 @@
:inactiveStyle="{ color: '#333' }"
v-model:current="current"
class="utabs"
:lineColor="$lightColor"
:activeStyle="{ color: $lightColor }"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
:bg-color="'#ffffff'"
></u-tabs>
</view>
<swiper class="swiper-box" :current="current" @change="changeSwiper" duration="500">
@@ -38,7 +39,7 @@
<view class="btn-view u-row-between" v-if="current == 2">
<view class="description">
<view class="text title">
<u-read-more ref="uReadMore" :color="$lightColor" text-indent="0">
<u-read-more ref="uReadMore" :color="lightColor" text-indent="0">
<rich-text :nodes="'评论内容:' + order.content || ''"></rich-text>
</u-read-more>
</view>
@@ -78,209 +79,144 @@
</view>
</template>
<script>
import { getOrderList } from "@/api/order.js";
import { getComments } from "@/api/members.js";
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getOrderList } from '@/api/order.js'
import { getComments } from '@/api/members.js'
export default {
data() {
return {
list: [
//顶部tab
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const list = [
{ name: '全部订单' },
{ name: '待评价' },
{ name: '已评价' },
]
const gradeList: Record<string, string> = {
GOOD: '好评',
MODERATE: '中评',
WORSE: '差评',
haveImage: '有图',
}
const groupCommentStatusWay: Record<string, string> = {
NEW: '新订单,不能进行评论',
UNFINISHED: '未完成评论',
WAIT_CHASE: '待追评的评论信息',
FINISHED: '已经完成评论',
}
const current = ref(0)
const orderList = ref<any[]>([])
const params = reactive<Record<string, any>>({
pageNumber: 1,
pageSize: 10,
loadStatus: 'more',
})
onShow(() => {
orderList.value = []
params.pageNumber = 1
current.value = 0
loadData()
})
watch(current, (val) => {
params.pageNumber = 1
params.loadStatus = 'more'
orderList.value = []
if (val == 0) {
delete params.commentStatus
loadData()
} else if (val == 1) {
params.commentStatus = 'UNFINISHED'
orderList.value = []
loadData()
} else {
params.commentStatus = 'FINISHED'
orderList.value = []
loadComments()
}
})
function preview(urls: string[], index: number) {
uni.previewImage({
current: index,
urls,
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
})
}
function changeSwiper(e: any) {
current.value = e.target.current
}
function loadData() {
uni.showLoading({ title: '加载中' })
getOrderList(params).then((res) => {
if (store.state.isShowToast) uni.hideLoading()
const records = res.data.result.records
if (records.length < 10) {
params.loadStatus = 'noMore'
}
if (records.length > 0) {
orderList.value = orderList.value.concat(records)
params.pageNumber += 1
}
})
}
function talkCommont(sku: any) {
uni.navigateTo({
url: `./releaseEvaluate?sn=${sku.sn}&sku=${encodeURIComponent(JSON.stringify(sku))}`,
})
}
function loadComments() {
uni.showLoading({ title: '加载中' })
getComments(params).then((res) => {
if (store.state.isShowToast) uni.hideLoading()
const records = res.data.result.records
if (records.length < 10) {
params.loadStatus = 'noMore'
}
records.forEach((item: any) => {
item.orderItems = [
{
name: "全部订单",
image: item.goodsImage,
name: item.goodsName,
goodsId: item.goodsId,
skuId: item.skuId,
},
{
name: "待评价",
},
{
name: "已评价",
},
],
gradeList: {
//评论表
GOOD: "好评",
MODERATE: "中评",
WORSE: "差评",
haveImage: "有图",
},
groupCommentStatusWay: {
NEW: "新订单,不能进行评论",
UNFINISHED: "未完成评论",
WAIT_CHASE: "待追评的评论信息",
FINISHED: "已经完成评论",
},
current: 0, //当前tabIndex
orderList: [], //商品集合
params: {
pageNumber: 1,
pageSize: 10,
loadStatus: "more",
},
};
},
]
})
orderList.value = orderList.value.concat(records)
params.pageNumber += 1
})
}
onShow() {
this.orderList = [];
this.params.pageNumber = 1;
this.current = 0
this.loadData()
},
watch: {
/**
* 切换current
* 更改页面并重新加载数据
*/
current(val) {
this.params.pageNumber = 1;
this.params.loadStatus = "more";
this.orderList = [];
//重新读取数据
function renderData(index: number) {
if (params.loadStatus == 'noMore') return
if (index == 0) {
loadData()
} else {
loadComments()
}
}
if (val == 0) {
delete this.params.commentStatus
this.loadData();
} else if (val == 1) {
this.params.commentStatus = "UNFINISHED";
this.orderList = [];
this.loadData();
} else {
this.params.commentStatus = "FINISHED";
this.orderList = [];
return this.loadComments();
}
},
},
methods: {
/**
* 判断当前店铺是否有可评价的商品
*/
commentStatus(val) {
if (this.current == 2) {
return true;
} else {
let show;
val.orderItems &&
val.orderItems.forEach((item) => {
if (item.commentStatus == "UNFINISHED") {
show = true;
} else {
show = false;
}
});
return show;
}
},
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
/**
* 点击swiper
*/
changeSwiper(e) {
this.current = e.target.current;
},
/**
* 获取订单数据
*/
loadData() {
uni.showLoading({
title: "加载中",
});
getOrderList(this.params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
const orderList = res.data.result.records;
if (orderList.length < 10) {
this.params.loadStatus = "noMore";
}
if (orderList.length > 0) {
this.orderList = this.orderList.concat(orderList);
this.params.pageNumber += 1;
}
});
},
/**
* 发表评价
*/
talkCommont(sku) {
console.log(sku);
uni.navigateTo({
url: `./releaseEvaluate?sn=${sku.sn}&sku=${encodeURIComponent(
JSON.stringify(sku)
)}`,
});
},
/**
* 加载已评价数据
*/
loadComments() {
uni.showLoading({
title: "加载中",
});
getComments(this.params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
let orderList = res.data.result.records;
if (orderList.length < 10) {
this.params.loadStatus = "noMore";
}
orderList.forEach((item) => {
item.orderItems = [
{
image: item.goodsImage,
name: item.goodsName,
goodsId: item.goodsId,
skuId: item.skuId,
},
];
});
this.orderList = this.orderList.concat(orderList);
this.params.pageNumber += 1;
});
},
/**
* 滑到底部加载数据
*/
renderData(index) {
if (this.params.loadStatus == "noMore") return;
if (index == 0) {
this.loadData();
} else {
this.loadComments();
}
},
/**
* 评价详情
*/
onDetail(comment) {
uni.navigateTo({
url:
"./evaluateDetail?comment=" +
encodeURIComponent(JSON.stringify(comment)),
});
},
},
};
function onDetail(comment: any) {
uni.navigateTo({
url: './evaluateDetail?comment=' + encodeURIComponent(JSON.stringify(comment)),
})
}
</script>
<style lang="scss" scoped>
page {
@@ -310,6 +246,27 @@ page {
.u-tabs-box {
position: relative;
z-index: 10;
background: #ffffff;
height: 88rpx;
box-shadow: 0 1rpx 8rpx rgba(0, 0, 0, 0.04);
}
:deep(.u-tabs),
:deep(.u-tabs__wrapper),
:deep(.u-tabs__wrapper__scroll-view-wrapper),
:deep(.u-tabs__wrapper__scroll-view),
:deep(.u-tabs__wrapper__nav) {
background: #fff;
height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item) {
min-height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item__text) {
color: #333;
font-size: 28rpx;
}
.box-content {
margin: 20rpx 0;

View File

@@ -86,80 +86,69 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { commentsMemberOrder } from "@/api/members.js";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { commentsMemberOrder } from '@/api/members.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
export default {
data() {
return {
storage,
type: "textarea", //输入框状态为 textarea
border: false, //没有border
maxlength: 500, //评价最大字数为500字
placeholder:
"宝贝满足您的期待吗?说说它的优点和美中不足的地方吧。您的评价会帮助更多的人",
sku: {}, //订单信息
form: {
content: "", //评价详情
goodsId: "", //商品id
grade: "GOOD", //默认为好评
orderItemSn: "", //商品的sn
skuId: "", //商品skuId
descriptionScore: 5, //默认描述得分为5分
serviceScore: 5, //默认服务得分为5分
deliveryScore: 5, //默认物流得分为5分
},
uploadFileList: [],
};
},
onLoad(options) {
// 获取上一级传过来的数据进行解析
this.form.orderItemSn = options.sn;
this.sku = JSON.parse(decodeURIComponent(options.sku));
this.form.goodsId = this.sku.goodsId;
this.form.skuId = this.sku.skuId;
},
methods: {
/**
* 点击评价
*/
onGrade(grade) {
this.form.grade = grade;
},
const store = useStore()
/**
* 提交评价
*/
onSubmit() {
uni.showLoading({
title: "加载中",
});
commentsMemberOrder(this.form).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (res.data.success) {
uni.showToast({
title: "发布评价成功",
duration: 2000,
icon: "none",
success: () => {
setTimeout(() => {
uni.navigateBack();
}, 500);
},
});
}
});
},
const type = 'textarea'
const border = false
const maxlength = 500
const placeholder =
'宝贝满足您的期待吗?说说它的优点和美中不足的地方吧。您的评价会帮助更多的人'
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.uploadFileList, (urls) => {
this.form.images = urls;
});
},
},
};
const sku = ref<Record<string, any>>({})
const form = reactive<Record<string, any>>({
content: '',
goodsId: '',
grade: 'GOOD',
orderItemSn: '',
skuId: '',
descriptionScore: 5,
serviceScore: 5,
deliveryScore: 5,
})
const uploadFileList = ref<any[]>([])
onLoad((options) => {
form.orderItemSn = options.sn
sku.value = JSON.parse(decodeURIComponent(options.sku))
form.goodsId = sku.value.goodsId
form.skuId = sku.value.skuId
})
function onGrade(grade: string) {
form.grade = grade
}
function onSubmit() {
uni.showLoading({ title: '加载中' })
commentsMemberOrder(form).then((res) => {
if (store.state.isShowToast) uni.hideLoading()
if (res.data.success) {
uni.showToast({
title: '发布评价成功',
duration: 2000,
icon: 'none',
success: () => {
setTimeout(() => {
uni.navigateBack()
}, 500)
},
})
}
})
}
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, uploadFileList.value, (urls) => {
form.images = urls
})
}
</script>
<style lang="scss" scoped>

View File

@@ -384,471 +384,426 @@
</div>
</div>
</template>
<script>
import * as API_Address from "@/api/address";
import * as API_Order from "@/api/order";
import * as API_Trade from "@/api/trade";
import configs from "@/config/config";
import LiLiWXPay from "@/js_sdk/lili-pay/wx-pay.js";
import invoices from "@/pages/order/invoice/setInvoice";
import { mapState } from "vuex";
export default {
onLoad: function (val) {
this.routerVal = val;
},
components: {
invoices,
},
<script setup lang="ts">
import { ref, computed, watch, getCurrentInstance } from 'vue'
import { onLoad, onShow, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Address from '@/api/address'
import * as API_Order from '@/api/order'
import * as API_Trade from '@/api/trade'
import configs from '@/config/config'
import LiLiWXPay from '@/js_sdk/lili-pay/wx-pay.js'
import invoices from '@/pages/order/invoice/setInvoice'
import {
unitPrice,
goodsFormatPrice,
secrecyMobile,
isLogin,
} from '@/utils/filters.js'
data() {
return {
configs,
userImage: configs.defaultUserPhoto,
invoiceFlag: false, //开票开关
shippingText: "LOGISTICS",
shippingFlag: false,
shippingMethod: [],
shippingWay: [
{
value: "LOGISTICS",
label: "物流",
},
{
value: "SELF_PICK_UP",
label: "自提",
},
],
isAssemble: false, //是否拼团
// 判断是否填写过备注
remarkFlag: false,
selectAddressId: "",
routerVal: "",
params: {},
// 优惠劵
couponList: "",
// 已选地址
address: "",
shopAddress: "",
// 发票信息
receiptList: "",
// 店铺信息
orderMessage: "",
data: "",
// 存储备注
remarkVal: [],
remarkVal1: "",
detail: "", //返回的所有数据
endWay: "", //最后一个参团人
masterWay: "", //团长信息
pintuanFlage: true, //是开团还是拼团
notSupportFreight: [], //不支持运费
notSupportFreightNoticeText: "",
storeAddress: "",
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
const aiderLightColor = computed(() => store.getters.aiderLightColor)
const remark = computed(() => store.state.remark)
originOrderData:"", // 原始订单数据
};
interface ShippingOption {
value: string
label: string
}
const shippingWay: ShippingOption[] = [
{ value: 'LOGISTICS', label: '物流' },
{ value: 'SELF_PICK_UP', label: '自提' },
]
const userImage = configs.defaultUserPhoto
const invoiceFlag = ref(false)
const shippingText = ref('LOGISTICS')
const shippingFlag = ref(false)
const shippingMethod = ref<ShippingOption[]>([])
const isAssemble = ref(false)
const remarkFlag = ref(false)
const selectAddressId = ref('')
const routerVal = ref<Record<string, any>>({})
const params = ref<Record<string, any>>({})
const couponList = ref('')
const address = ref<any>('')
const shopAddress = ref('')
const receiptList = ref<any>('')
const orderMessage = ref<any>('')
const data = ref('')
const remarkVal = ref<any[]>([])
const remarkVal1 = ref('')
const detail = ref('')
const endWay = ref<any>('')
const masterWay = ref<any>('')
const pintuanFlage = ref(true)
const notSupportFreight = ref<any[]>([])
const notSupportFreightNoticeText = ref('')
const storeAddress = ref<any>('')
const originOrderData = ref<any>('')
watch(
remarkVal,
(val) => {
store.commit('setRemark', val)
},
watch: {
// 监听备注 并在 vuex 中存储
remarkVal: {
handler(val) {
this.$store.commit("setRemark", val);
},
immediate: true,
deep: true,
},
},
computed: {
...mapState(["remark"]),
},
/**
* 监听返回
*/
onBackPress(e) {
if (e.from == "backbutton") {
const curRoute = getCurrentPages().slice(-1)[0]?.options || {};
if (curRoute.addId) {
uni.reLaunch({
url: "/pages/tabbar/cart/cartList",
});
} else if (this.routerVal?.way === "CART") {
uni.switchTab({
url: "/pages/tabbar/cart/cartList",
});
} else {
uni.navigateBack();
}
return true;
{ immediate: true, deep: true }
)
onLoad((val) => {
routerVal.value = val || {}
})
onBackPress((e) => {
if (e.from == 'backbutton') {
const curRoute = getCurrentPages().slice(-1)[0]?.options || {}
if (curRoute.addId) {
uni.reLaunch({
url: '/pages/tabbar/cart/cartList',
})
} else if (routerVal.value?.way === 'CART') {
uni.switchTab({
url: '/pages/tabbar/cart/cartList',
})
} else {
uni.navigateBack()
}
},
return true
}
return false
})
async onShow() {
// 判断是否存在写过备注信息的商品
if (this.remark && this.remark.length > 0) {
this.remarkFlag = true;
onShow(async () => {
if (remark.value && remark.value.length > 0) {
remarkFlag.value = true
}
uni.showLoading({
mask: true,
})
try {
await getOrderList()
await getDistribution()
if (routerVal.value.way == 'PINTUAN') {
isAssemble.value = true
routerVal.value.parentOrder = JSON.parse(
decodeURIComponent(routerVal.value.parentOrder)
)
pintuanWay()
}
uni.showLoading({
mask: true,
});
try {
await this.getOrderList();
await this.getDistribution();
if (this.routerVal.way == "PINTUAN") {
this.isAssemble = true;
this.routerVal.parentOrder = JSON.parse(
decodeURIComponent(this.routerVal.parentOrder)
);
this.pintuanWay();
}
} finally {
uni.hideLoading();
} finally {
uni.hideLoading()
}
})
function getShippingLabel() {
const item =
shippingMethod.value.find((e) => e.value === shippingText.value) ||
shippingWay.find((e) => e.value === shippingText.value)
return item ? item.label : ''
}
async function callbackInvoice(val: any) {
invoiceFlag.value = false
receiptList.value = val
if (val) {
const submit = {
way: routerVal.value.way,
...receiptList.value,
}
},
mounted() {},
const receipt = await API_Order.getReceipt(submit)
if (receipt.data.success) {
shippingFlag.value = false
getOrderList()
}
}
}
methods: {
getShippingLabel() {
const item =
this.shippingMethod.find((e) => e.value === this.shippingText) ||
this.shippingWay.find((e) => e.value === this.shippingText);
return item ? item.label : "";
},
function navigateToStore(val: any) {
uni.navigateTo({
url: '/pages/product/shopPage?id=' + val.storeId,
})
}
//发票回调 选择发票之后刷新购物车
async callbackInvoice(val) {
this.invoiceFlag = false;
this.receiptList = val;
if (val) {
let submit = {
way: this.routerVal.way,
...this.receiptList,
};
let receipt = await API_Order.getReceipt(submit);
if (receipt.data.success) {
this.shippingFlag = false;
this.getOrderList();
}
function clickToAddress() {
navigateTo(
`/pages/mine/address/address?from=cart&way=${
routerVal.value.way
}&parentOrder=${encodeURIComponent(
JSON.stringify(routerVal.value.parentOrder)
)}`
)
}
function clickToStoreAddress() {
navigateTo(
`/pages/mine/address/storeAddress?from=cart&way=${routerVal.value.way}&storeId=${remarkVal.value[0].storeId}`
)
}
function pintuanWay() {
const { memberId } = routerVal.value.parentOrder
const userInfo = isLogin()
if (memberId) {
endWay.value = userInfo
masterWay.value = routerVal.value.parentOrder
pintuanFlage.value = false
} else {
pintuanFlage.value = true
masterWay.value = userInfo
}
}
function invoice() {
invoiceFlag.value = true
}
function GET_Discount() {
let storeIds: any[] = []
let skus: any[] = []
const selectedCoupon: any[] = []
if (orderMessage.value.platformCoupon) {
selectedCoupon.push(orderMessage.value.platformCoupon.memberCoupon.id)
}
if (
orderMessage.value.storeCoupons &&
Object.keys(orderMessage.value.storeCoupons)[0]
) {
const storeMemberCouponsId = Object.keys(
orderMessage.value.storeCoupons
)[0]
const storeCouponId =
orderMessage.value.storeCoupons[storeMemberCouponsId].memberCoupon.id
selectedCoupon.push(storeCouponId)
}
orderMessage.value.cartList.forEach((item: any) => {
item.skuList.forEach((sku: any) => {
storeIds.push(sku.storeId)
skus.push(sku.goodsSku.id)
})
})
storeIds = Array.from(new Set(storeIds))
skus = Array.from(new Set(skus))
uni.setStorage({
key: 'totalPrice',
data: orderMessage.value.priceDetailDTO.goodsPrice,
})
navigateTo(
`/pages/cart/coupon/index?way=${routerVal.value.way}&storeId=${storeIds}&skuId=${skus}&selectedCoupon=${selectedCoupon}`
)
}
function navigateTo(url: string) {
uni.navigateTo({
url,
})
}
function createTradeFun() {
proxy!.$u.throttle(() => {
if (shippingText.value === 'SELF_PICK_UP') {
if (!storeAddress.value.id) {
uni.showToast({
title: '请选择提货点',
duration: 2000,
icon: 'none',
})
return false
}
},
// 跳转到店铺
navigateToStore(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
// 点击跳转地址
clickToAddress() {
this.navigateTo(
`/pages/mine/address/address?from=cart&way=${
this.routerVal.way
}&parentOrder=${encodeURIComponent(
JSON.stringify(this.routerVal.parentOrder)
)}`
);
},
clickToStoreAddress() {
this.navigateTo(
`/pages/mine/address/storeAddress?from=cart&way=${this.routerVal.way}&storeId=${this.remarkVal[0].storeId}`
);
},
// 判断团长以及团员信息
pintuanWay() {
const { memberId } = this.routerVal.parentOrder;
const userInfo = this.isLogin();
if (memberId) {
this.endWay = userInfo;
this.masterWay = this.routerVal.parentOrder;
this.pintuanFlage = false;
} else {
this.pintuanFlage = true;
this.masterWay = userInfo;
} else if (
shippingText.value === 'LOGISTICS' &&
orderMessage.value.cartTypeEnum !== 'VIRTUAL'
) {
if (!address.value.id) {
uni.showToast({
title: '请选择地址',
duration: 2000,
icon: 'none',
})
return false
}
},
// 判断发票
invoice() {
this.invoiceFlag = true;
},
}
// 领取优惠券
GET_Discount() {
// 循环店铺id,商品id获取优惠券
let store = [];
let skus = [];
let selectedCoupon = [];
if (this.orderMessage.platformCoupon)
selectedCoupon.push(this.orderMessage.platformCoupon.memberCoupon.id);
if (
this.orderMessage.storeCoupons &&
Object.keys(this.orderMessage.storeCoupons)[0]
) {
let storeMemberCouponsId = Object.keys(
this.orderMessage.storeCoupons
)[0];
let storeCouponId =
this.orderMessage.storeCoupons[storeMemberCouponsId].memberCoupon.id;
selectedCoupon.push(storeCouponId);
}
this.orderMessage.cartList.forEach((item) => {
item.skuList.forEach((sku) => {
store.push(sku.storeId);
skus.push(sku.goodsSku.id);
});
});
store = Array.from(new Set(store));
skus = Array.from(new Set(skus));
uni.setStorage({
key: "totalPrice",
data: this.orderMessage.priceDetailDTO.goodsPrice,
});
this.navigateTo(
`/pages/cart/coupon/index?way=${this.routerVal.way}&storeId=${store}&skuId=${skus}&selectedCoupon=${selectedCoupon}`
);
},
let client
// #ifdef H5
client = 'H5'
// #endif
// #ifdef MP-WEIXIN
client = 'WECHAT_MP'
// #endif
// #ifdef APP-PLUS
client = 'APP'
// #endif
/**
* 跳转
*/
navigateTo(url) {
uni.navigateTo({
url,
});
},
const submit: Record<string, any> = {
client,
way: routerVal.value.way,
remark: remarkVal.value,
parentOrderSn: '',
}
if (routerVal.value.parentOrder && routerVal.value.parentOrder.orderSn) {
submit.parentOrderSn = routerVal.value.parentOrder.orderSn
} else {
delete submit.parentOrderSn
}
/**
* 提交订单准备支付
*/
// 创建订单
createTradeFun() {
// 防抖
this.$u.throttle(() => {
if (this.shippingText === "SELF_PICK_UP") {
if (!this.storeAddress.id) {
uni.showToast({
title: "请选择提货点",
duration: 2000,
icon: "none",
});
return false;
}
} else if (this.shippingText === "LOGISTICS" && this.orderMessage.cartTypeEnum !== 'VIRTUAL') {
if (!this.address.id) {
uni.showToast({
title: "请选择地址",
duration: 2000,
icon: "none",
});
return false;
}
}
// 创建订单
let client;
// #ifdef H5
client = "H5";
// #endif
// #ifdef MP-WEIXIN
client = "WECHAT_MP";
// #endif
// #ifdef APP-PLUS
client = "APP";
// #endif
let submit = {
client,
way: this.routerVal.way,
remark: this.remarkVal,
parentOrderSn: "",
};
// 如果是拼团并且当前用户不是团长
this.routerVal.parentOrder && this.routerVal.parentOrder.orderSn
? (submit.parentOrderSn = this.routerVal.parentOrder.orderSn)
: delete submit.parentOrderSn;
/**
* 创建订单
*/
API_Trade.createTrade(submit).then((res) => {
if (res.data.success) {
uni.showToast({
title: "创建订单成功!",
duration: 2000,
icon: "none",
});
// 如果当前价格为0跳转到订单列表
if (this.orderMessage.priceDetailDTO.billPrice == 0) {
uni.navigateTo({
url: "/pages/order/myOrder?status=0",
});
} else {
// #ifdef MP-WEIXIN
// 微信小程序中点击创建订单直接开始支付
this.pay(res.data.result.sn);
// #endif
// #ifndef MP-WEIXIN
this.navigateTo(
`/pages/cart/payment/payOrder?trade_sn=${res.data.result.sn}`
);
// #endif
}
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
}, 3000);
},
/**
* 微信小程序中直接支付
*/
async pay(sn) {
new LiLiWXPay({
sn: sn,
price: this.orderMessage.priceDetailDTO.billPrice,
}).pay();
},
/**
* 获取用户地址
*/
getUserAddress() {
// 如果没有商品选择地址的话 则选择 默认地址
API_Address.getAddressDefault().then((res) => {
if (res.data.result) {
res.data.result.consigneeAddressPath =
res.data.result.consigneeAddressPath.split(",");
this.address = res.data.result;
}
});
},
// 获取配送列表
async getDistribution() {
let shopRes = await API_Trade.shippingMethodList({
way: this.routerVal.way,
});
let shopList;
if (shopRes.data.success) {
shopList = shopRes.data.result;
let way = [];
console.log(shopList);
this.shippingWay.forEach((item) => {
shopList.forEach((child) => {
if (item.value == child) {
way.push(item);
}
});
});
this.shippingMethod = way;
if (way.length && !way.some((item) => item.value === this.shippingText)) {
this.shippingText = way[0].value;
}
}
},
// 选择配送
async confirmDistribution(val) {
const selected = val?.value?.[0] || val?.[0];
if (!selected?.value) {
return;
}
let res = await API_Trade.setShipMethod({
shippingMethod: selected.value,
way: this.routerVal.way,
});
this.shippingText = selected.value;
API_Trade.createTrade(submit).then((res) => {
if (res.data.success) {
this.getOrderList();
}
},
// 获取结算参数
getOrderList() {
this.notSupportFreight = [];
this.notSupportFreightNoticeText = "";
return API_Trade.getCheckoutParams(this.routerVal.way).then((res) => {
// 获取结算参数 进行首次判断
this.originOrderData = this.orderMessage
? JSON.parse(JSON.stringify(this.orderMessage))
: null;
if (
!res.data.result.checkedSkuList ||
res.data.result.checkedSkuList.length === 0
) {
if (!this.originOrderData?.checkedSkuList?.length) {
uni.switchTab({
url: "/pages/tabbar/cart/cartList",
});
}
}
if (res.data.result.skuList.length <= 0) {
if (!this.originOrderData?.skuList?.length) {
uni.navigateTo({
url: "/pages/order/myOrder?status=0",
});
}
}
let repeatData;
res.data.result.cartList.forEach((item, index) => {
// 如果已经写过备注信息的话赋值
repeatData = {
remark: this.remarkFlag
? this.remark[index].storeId == item.storeId
? this.remark[index].remark
: item.remark
: item.remark,
storeId: item.storeId,
};
this.remarkVal[index] = repeatData;
});
this.orderMessage = res.data.result;
/**
* 为了避免路径传值在h5中超出限制问题
* 这块将可用的优惠券以及不可用的优惠券放入到vuex里面进行存储
*/
this.$store.state.canUseCoupons = res.data.result.canUseCoupons;
this.$store.state.cantUseCoupons = res.data.result.cantUseCoupons;
if (!res.data.result.memberAddress) {
// 获取会员默认地址
this.getUserAddress();
uni.showToast({
title: '创建订单成功!',
duration: 2000,
icon: 'none',
})
if (orderMessage.value.priceDetailDTO.billPrice == 0) {
uni.navigateTo({
url: '/pages/order/myOrder?status=0',
})
} else {
this.address = res.data.result.memberAddress;
res.data.result.memberAddress.consigneeAddressPath =
res.data.result.memberAddress.consigneeAddressPath.split(",");
}
if (res.data.result.storeAddress) {
this.storeAddress = res.data.result.storeAddress;
console.log("storeAddress", this.storeAddress);
}
if (
res.data.result.notSupportFreight &&
res.data.result.notSupportFreight.length != 0
) {
this.notSupportFreight = res.data.result.notSupportFreight;
this.notSupportFreightNoticeText = "以下商品超出配送范围:";
res.data.result.notSupportFreight.forEach((item) => {
this.notSupportFreightNoticeText += item.goodsSku.goodsName;
});
}
});
},
// #ifdef MP-WEIXIN
pay(res.data.result.sn)
// #endif
//
},
};
// #ifndef MP-WEIXIN
navigateTo(
`/pages/cart/payment/payOrder?trade_sn=${res.data.result.sn}`
)
// #endif
}
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}, 3000)
}
async function pay(sn: string) {
new LiLiWXPay({
sn,
price: orderMessage.value.priceDetailDTO.billPrice,
}).pay()
}
function getUserAddress() {
API_Address.getAddressDefault().then((res) => {
if (res.data.result) {
res.data.result.consigneeAddressPath =
res.data.result.consigneeAddressPath.split(',')
address.value = res.data.result
}
})
}
async function getDistribution() {
const shopRes = await API_Trade.shippingMethodList({
way: routerVal.value.way,
})
if (shopRes.data.success) {
const shopList = shopRes.data.result
const way: ShippingOption[] = []
console.log(shopList)
shippingWay.forEach((item) => {
shopList.forEach((child: string) => {
if (item.value == child) {
way.push(item)
}
})
})
shippingMethod.value = way
if (way.length && !way.some((item) => item.value === shippingText.value)) {
shippingText.value = way[0].value
}
}
}
async function confirmDistribution(val: any) {
const selected = val?.value?.[0] || val?.[0]
if (!selected?.value) {
return
}
const res = await API_Trade.setShipMethod({
shippingMethod: selected.value,
way: routerVal.value.way,
})
shippingText.value = selected.value
if (res.data.success) {
getOrderList()
}
}
function getOrderList() {
notSupportFreight.value = []
notSupportFreightNoticeText.value = ''
return API_Trade.getCheckoutParams(routerVal.value.way).then((res) => {
originOrderData.value = orderMessage.value
? JSON.parse(JSON.stringify(orderMessage.value))
: null
if (
!res.data.result.checkedSkuList ||
res.data.result.checkedSkuList.length === 0
) {
if (!originOrderData.value?.checkedSkuList?.length) {
uni.switchTab({
url: '/pages/tabbar/cart/cartList',
})
}
}
if (res.data.result.skuList.length <= 0) {
if (!originOrderData.value?.skuList?.length) {
uni.navigateTo({
url: '/pages/order/myOrder?status=0',
})
}
}
let repeatData
res.data.result.cartList.forEach((item: any, index: number) => {
repeatData = {
remark: remarkFlag.value
? remark.value[index].storeId == item.storeId
? remark.value[index].remark
: item.remark
: item.remark,
storeId: item.storeId,
}
remarkVal.value[index] = repeatData
})
orderMessage.value = res.data.result
;(store.state as any).canUseCoupons = res.data.result.canUseCoupons
;(store.state as any).cantUseCoupons = res.data.result.cantUseCoupons
if (!res.data.result.memberAddress) {
getUserAddress()
} else {
address.value = res.data.result.memberAddress
res.data.result.memberAddress.consigneeAddressPath =
res.data.result.memberAddress.consigneeAddressPath.split(',')
}
if (res.data.result.storeAddress) {
storeAddress.value = res.data.result.storeAddress
console.log('storeAddress', storeAddress.value)
}
if (
res.data.result.notSupportFreight &&
res.data.result.notSupportFreight.length != 0
) {
notSupportFreight.value = res.data.result.notSupportFreight
notSupportFreightNoticeText.value = '以下商品超出配送范围:'
res.data.result.notSupportFreight.forEach((item: any) => {
notSupportFreightNoticeText.value += item.goodsSku.goodsName
})
}
})
}
</script>
<style scoped>
page {

View File

@@ -74,185 +74,128 @@
</view>
</template>
<script>
import { getReceiptDetail } from "@/api/order.js";
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getReceiptDetail } from '@/api/order.js'
import { unitPrice } from '@/utils/filters.js'
export default {
data() {
return {
order: {},
title_type: "",
showInvoicePopup: false,
};
},
onLoad(options) {
this.loadData(options.id);
},
methods: {
loadData(id) {
getReceiptDetail(id).then((res) => {
let order = res.data.result;
this.order = order;
this.title_type = order.companyName || order.taxpayerId ? "单位" : "个人";
});
},
getTitleNameValue() {
return this.title_type === "单位"
? this.order.companyName || "-"
: this.order.personalName || "-";
},
viewInvoice() {
if (!this.order.invoiceAddress) {
const order = ref<Record<string, any>>({})
const title_type = ref('')
const showInvoicePopup = ref(false)
onLoad((options) => {
loadData(options.id)
})
function loadData(id: string) {
getReceiptDetail(id).then((res) => {
const result = res.data.result
order.value = result
title_type.value = result.companyName || result.taxpayerId ? '单位' : '个人'
})
}
function getTitleNameValue() {
return title_type.value === '单位'
? order.value.companyName || '-'
: order.value.personalName || '-'
}
function viewInvoice() {
if (!order.value.invoiceAddress) {
uni.showToast({
title: '暂无发票地址',
duration: 2000,
icon: 'none',
})
return
}
if (isImageInvoice()) {
showInvoicePopup.value = true
return
}
// #ifdef APP-PLUS
plus.runtime.openURL(order.value.invoiceAddress)
// #endif
// #ifndef APP-PLUS
uni.navigateTo({
url: '/pages/tabbar/home/web-view?src=' + encodeURIComponent(order.value.invoiceAddress),
})
// #endif
}
function isImageInvoice() {
const url = (order.value.invoiceAddress || '').split('?')[0].toLowerCase()
return /\.(png|jpe?g|gif|bmp|webp)$/.test(url)
}
function previewImageInvoice() {
if (!order.value.invoiceAddress) return
uni.previewImage({
current: 0,
urls: [order.value.invoiceAddress],
})
}
function downloadImageInvoice() {
if (!order.value.invoiceAddress) {
uni.showToast({
title: '暂无发票可下载',
duration: 2000,
icon: 'none',
})
return
}
uni.downloadFile({
url: order.value.invoiceAddress,
success: (res) => {
if (res.statusCode !== 200) {
uni.showToast({
title: "暂无发票地址",
title: '下载失败',
duration: 2000,
icon: "none",
});
return;
icon: 'none',
})
return
}
if (this.isImageInvoice()) {
this.showInvoicePopup = true;
return;
}
// #ifdef APP-PLUS
plus.runtime.openURL(this.order.invoiceAddress);
const tempFilePath = res.tempFilePath
// #ifdef H5
const link = document.createElement('a')
link.href = tempFilePath || order.value.invoiceAddress
link.download = 'invoice'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// #endif
// #ifndef APP-PLUS
uni.navigateTo({
url:
"/pages/tabbar/home/web-view?src=" +
encodeURIComponent(this.order.invoiceAddress),
});
// #endif
},
isImageInvoice() {
const url = (this.order.invoiceAddress || "").split("?")[0].toLowerCase();
return /\.(png|jpe?g|gif|bmp|webp)$/.test(url);
},
previewImageInvoice() {
if (!this.order.invoiceAddress) {
return;
}
uni.previewImage({
current: 0,
urls: [this.order.invoiceAddress],
});
},
downloadImageInvoice() {
if (!this.order.invoiceAddress) {
uni.showToast({
title: "暂无发票可下载",
duration: 2000,
icon: "none",
});
return;
}
uni.downloadFile({
url: this.order.invoiceAddress,
success: (res) => {
if (res.statusCode !== 200) {
uni.showToast({
title: "下载失败",
duration: 2000,
icon: "none",
});
return;
}
const tempFilePath = res.tempFilePath;
// #ifdef H5
const link = document.createElement("a");
link.href = tempFilePath || this.order.invoiceAddress;
link.download = "invoice";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: () => {
uni.showToast({
title: "发票已保存到相册",
duration: 2000,
icon: "none",
});
},
fail: () => {
uni.showToast({
title: "保存失败",
duration: 2000,
icon: "none",
});
},
});
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: () => {
uni.showToast({
title: '发票已保存到相册',
duration: 2000,
icon: 'none',
})
},
fail: () => {
uni.showToast({
title: "下载失败",
title: '保存失败',
duration: 2000,
icon: "none",
});
icon: 'none',
})
},
});
})
// #endif
},
/**
* 点击图片放大或保存
*/
preview() {
//预览发票
if (this.order.elec_file_list.length) {
uni.previewImage({
current: 0,
urls: this.order.elec_file_list,
longPressActions: {
itemList: ["发送给朋友", "保存图片", "收藏"],
success: function (data) {},
fail: function (err) {},
},
});
} else {
uni.showToast({
title: "暂无发票可预览",
duration: 2000,
icon: "none",
});
}
fail: () => {
uni.showToast({
title: '下载失败',
duration: 2000,
icon: 'none',
})
},
download() {
//下载发票
let _this = this;
if (this.order.elec_file_list.length) {
this.order.elec_file_list.forEach((item) => {
uni.downloadFile({
url: item,
success: (res) => {
if (res.statusCode === 200) {
let tempFilePath = res.tempFilePath;
uni.saveFile({
tempFilePath: tempFilePath,
success: function (res) {
uni.showToast({
title: "发票已下载到" + res.savedFilePath,
duration: 2000,
icon: "none",
});
},
});
}
},
});
});
} else {
uni.showToast({
title: "暂无发票可下载",
duration: 2000,
icon: "none",
});
}
},
},
};
})
}
</script>
<style lang="scss" scoped>

View File

@@ -92,340 +92,355 @@
</div>
</u-popup>
</template>
<script>
export default {
props: ["res"],
computed: {
isSpecialInvoice() {
return this.getActiveTitle(this.invoiceType) === "增值税专用发票";
},
titleName: {
get() {
return this.isUnitTitle()
? this.submitData.companyName
: this.submitData.personalName;
},
set(value) {
if (this.isUnitTitle()) {
this.submitData.companyName = value;
} else {
this.submitData.personalName = value;
}
this.syncReceiptTitle();
},
},
<script setup lang="ts">
import { ref, reactive, computed, watch, onMounted, getCurrentInstance } from 'vue'
interface InvoiceOption {
title: string
active: boolean
disabled?: boolean
}
interface SubmitData {
receiptTitle: string
receiptType: string
personalName: string
companyName: string
taxpayerId: string
receiptContent: string
companyAddress: string
companyPhone: string
bankName: string
bankAccount: string
receiptPhone: string
receiptEmail: string
}
const props = defineProps<{
res?: Record<string, any>
}>()
const emit = defineEmits<{
callbackInvoice: [val: SubmitData | boolean]
}>()
const { proxy } = getCurrentInstance()!
const shouldClearOnTypeChange = ref(false)
const taxpayerFlag = ref(false)
const submitData = reactive<SubmitData>({
receiptTitle: '',
receiptType: '1',
personalName: '',
companyName: '',
taxpayerId: '',
receiptContent: '',
companyAddress: '',
companyPhone: '',
bankName: '',
bankAccount: '',
receiptPhone: '',
receiptEmail: '',
})
const show = ref(true)
const title = ref('')
const tips =
'电子发票即电子增值税发票,是税局认可的有效凭证,其法律效力、基本用途及使用规定同纸质发票。'
const invoiceType = reactive<InvoiceOption[]>([
{ title: '电子普通发票', active: true },
{ title: '增值税专用发票', active: false },
])
const invoiceHeader = reactive<InvoiceOption[]>([
{ title: '个人', active: false },
{ title: '单位', active: false },
])
const goodsType = reactive<InvoiceOption[]>([
{ title: '商品明细', active: false },
{ title: '商品类别', active: false },
])
const isSpecialInvoice = computed(
() => getActiveTitle(invoiceType) === '增值税专用发票'
)
const titleName = computed({
get() {
return isUnitTitle() ? submitData.companyName : submitData.personalName
},
watch: {
invoiceType: {
handler(val) {
const currentType = this.getActiveTitle(val);
const nextReceiptType =
currentType === "增值税专用发票" ? "2" : "1";
const previousReceiptType = this.submitData.receiptType;
this.submitData.receiptType = nextReceiptType;
if (
this.shouldClearOnTypeChange &&
previousReceiptType &&
previousReceiptType !== nextReceiptType
) {
this.clearInvoiceInfo();
}
if (currentType === "增值税专用发票") {
this.setActiveByTitle(this.invoiceHeader, "单位");
this.setActiveByTitle(this.goodsType, "商品明细");
this.title = "单位";
this.taxpayerFlag = true;
this.submitData.receiptContent = "商品明细";
this.syncReceiptTitle();
} else {
this.setActiveByTitle(this.invoiceHeader, "个人");
this.setActiveByTitle(this.goodsType, "商品明细");
this.title = "个人";
this.taxpayerFlag = false;
this.submitData.receiptContent = "商品明细";
this.syncReceiptTitle();
}
this.shouldClearOnTypeChange = false;
},
deep: true,
},
invoiceHeader: {
handler(val) {
if (this.isSpecialInvoice) {
this.title = "单位";
this.taxpayerFlag = true;
return;
}
this.title = this.getActiveTitle(val) || "个人";
this.taxpayerFlag = this.title == "单位";
if (!this.taxpayerFlag) {
this.submitData.taxpayerId = "";
}
this.syncReceiptTitle();
},
deep: true,
},
goodsType: {
handler(val) {
this.submitData.receiptContent = val.filter((item) => {
return item.active == true;
})[0].title;
},
deep: true,
},
},
data() {
return {
shouldClearOnTypeChange: false,
taxpayerFlag: false,
submitData: {
receiptTitle: "", //发票抬头
receiptType: "1", // 发票类型
personalName: "",
companyName: "",
taxpayerId: "", //纳税人
receiptContent: "",
companyAddress: "", //单位地址
companyPhone: "", //单位电话
bankName: "", //开户银行
bankAccount: "", //银行账号
receiptPhone: "", //收票人手机
receiptEmail: "", //收票人邮箱
},
show: true,
title: "",
tips:
"电子发票即电子增值税发票,是税局认可的有效凭证,其法律效力、基本用途及使用规定同纸质发票。",
// 发票类型
invoiceType: [
{
title: "电子普通发票",
active: true,
},
{
title: "增值税专用发票",
active: false,
},
],
// 发票抬头
invoiceHeader: [
{
title: "个人",
active: false,
},
{
title: "单位",
active: false,
},
],
// 商品类型
goodsType: [
{
title: "商品明细",
active: false,
},
{
title: "商品类别",
active: false,
},
],
};
},
mounted() {
if (this.res) {
this.submitData.receiptType = this.normalizeReceiptType(this.res.receiptType);
this.submitData.personalName =
this.res.personalName ||
(!this.res.companyName && !this.res.taxpayerId ? this.res.receiptTitle || "" : "");
this.submitData.companyName =
this.res.companyName ||
(this.res.taxpayerId ? this.res.receiptTitle || "" : "");
this.submitData.taxpayerId = this.res.taxpayerId; //纳税人
this.submitData.receiptContent = this.res.receiptContent;
this.submitData.companyAddress = this.res.companyAddress || "";
this.submitData.companyPhone = this.res.companyPhone || "";
this.submitData.bankName = this.res.bankName || "";
this.submitData.bankAccount = this.res.bankAccount || "";
this.submitData.receiptPhone = this.res.receiptPhone || "";
this.submitData.receiptEmail = this.res.receiptEmail || "";
if (this.submitData.receiptType === "2") {
this.setActiveByTitle(this.invoiceType, "增值税专用发票");
this.setActiveByTitle(this.invoiceHeader, "单位");
this.setActiveByTitle(this.goodsType, "商品明细");
} else {
this.setActiveByTitle(this.invoiceType, "电子普通发票");
this.res.receiptContent == "商品类别"
? this.setActiveByTitle(this.goodsType, "商品类别")
: this.setActiveByTitle(this.goodsType, "商品明细");
this.res.taxpayerId
? this.setActiveByTitle(this.invoiceHeader, "单位")
: this.setActiveByTitle(this.invoiceHeader, "个人");
}
this.syncReceiptTitle();
set(value: string) {
if (isUnitTitle()) {
submitData.companyName = value
} else {
this.setActiveByTitle(this.invoiceType, "电子普通发票");
this.setActiveByTitle(this.invoiceHeader, "个人");
this.setActiveByTitle(this.goodsType, "商品明细");
this.syncReceiptTitle();
submitData.personalName = value
}
syncReceiptTitle()
},
})
watch(
invoiceType,
(val) => {
const currentType = getActiveTitle(val)
const nextReceiptType = currentType === '增值税专用发票' ? '2' : '1'
const previousReceiptType = submitData.receiptType
submitData.receiptType = nextReceiptType
if (
shouldClearOnTypeChange.value &&
previousReceiptType &&
previousReceiptType !== nextReceiptType
) {
clearInvoiceInfo()
}
if (currentType === '增值税专用发票') {
setActiveByTitle(invoiceHeader, '单位')
setActiveByTitle(goodsType, '商品明细')
title.value = '单位'
taxpayerFlag.value = true
submitData.receiptContent = '商品明细'
syncReceiptTitle()
} else {
setActiveByTitle(invoiceHeader, '个人')
setActiveByTitle(goodsType, '商品明细')
title.value = '个人'
taxpayerFlag.value = false
submitData.receiptContent = '商品明细'
syncReceiptTitle()
}
shouldClearOnTypeChange.value = false
},
methods: {
normalizeReceiptType(type) {
return type === "2" || type === "VATOSPECIAL" ? "2" : "1";
},
getActiveTitle(list) {
const current = list.find((item) => item.active);
return current ? current.title : "";
},
setActiveByTitle(list, title) {
list.forEach((item) => {
item.active = item.title === title;
});
},
isUnitTitle() {
return this.isSpecialInvoice || this.title === "单位";
},
syncReceiptTitle() {
this.submitData.receiptTitle = this.isUnitTitle()
? this.submitData.companyName
: this.submitData.personalName;
},
clearInvoiceInfo() {
this.submitData.receiptTitle = "";
this.submitData.personalName = "";
this.submitData.companyName = "";
this.submitData.taxpayerId = "";
this.submitData.receiptContent = "";
this.submitData.companyAddress = "";
this.submitData.companyPhone = "";
this.submitData.bankName = "";
this.submitData.bankAccount = "";
this.submitData.receiptPhone = "";
this.submitData.receiptEmail = "";
},
handleClickHeader(val, index, arr) {
if (val.disabled) {
return;
}
const previousTitle = this.getActiveTitle(arr);
if (arr === this.invoiceType && previousTitle !== val.title) {
this.shouldClearOnTypeChange = true;
}
arr.forEach((item) => {
item.active = false;
});
val.active = true;
},
/**
* 监听关闭
*/
close(val) {
this.$emit("callbackInvoice", val);
},
submitInvoice() {
/**
* 验证
*/
const {
receiptTitle,
taxpayerId,
companyAddress,
companyPhone,
bankName,
bankAccount,
receiptPhone,
receiptEmail,
} = this.submitData;
this.syncReceiptTitle();
{ deep: true }
)
if (this.$u.test.isEmpty(receiptTitle)) {
uni.showToast({
title: "请您填写发票抬头!",
duration: 2000,
icon: "none",
});
return false;
}
if (
!this.$u.test.isEmpty(receiptTitle) &&
this.$u.test.isEmpty(taxpayerId) &&
this.invoiceHeader[1].active == true
) {
uni.showToast({
title: "请您填写纳税人识别号!",
duration: 2000,
icon: "none",
});
watch(
invoiceHeader,
(val) => {
if (isSpecialInvoice.value) {
title.value = '单位'
taxpayerFlag.value = true
return
}
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(companyAddress)) {
uni.showToast({
title: "请您填写单位地址!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(companyPhone)) {
uni.showToast({
title: "请您填写单位电话!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(bankName)) {
uni.showToast({
title: "请您填写开户银行!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(bankAccount)) {
uni.showToast({
title: "请您填写银行账号!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.$u.test.isEmpty(receiptPhone)) {
uni.showToast({
title: "请您填写收票人手机!",
duration: 2000,
icon: "none",
});
return false;
}
if (!this.$u.test.mobile(receiptPhone)) {
uni.showToast({
title: "请输入正确的收票人手机号!",
duration: 2000,
icon: "none",
});
return false;
}
if (!this.$u.test.isEmpty(receiptEmail) && !this.$u.test.email(receiptEmail)) {
uni.showToast({
title: "请输入正确的收票人邮箱!",
duration: 2000,
icon: "none",
});
return false;
}
this.show = false;
this.close(this.submitData);
},
title.value = getActiveTitle(val) || '个人'
taxpayerFlag.value = title.value == '单位'
if (!taxpayerFlag.value) {
submitData.taxpayerId = ''
}
syncReceiptTitle()
},
};
{ deep: true }
)
watch(
goodsType,
(val) => {
submitData.receiptContent = val.filter((item) => item.active == true)[0].title
},
{ deep: true }
)
onMounted(() => {
if (props.res) {
submitData.receiptType = normalizeReceiptType(props.res.receiptType)
submitData.personalName =
props.res.personalName ||
(!props.res.companyName && !props.res.taxpayerId
? props.res.receiptTitle || ''
: '')
submitData.companyName =
props.res.companyName ||
(props.res.taxpayerId ? props.res.receiptTitle || '' : '')
submitData.taxpayerId = props.res.taxpayerId
submitData.receiptContent = props.res.receiptContent
submitData.companyAddress = props.res.companyAddress || ''
submitData.companyPhone = props.res.companyPhone || ''
submitData.bankName = props.res.bankName || ''
submitData.bankAccount = props.res.bankAccount || ''
submitData.receiptPhone = props.res.receiptPhone || ''
submitData.receiptEmail = props.res.receiptEmail || ''
if (submitData.receiptType === '2') {
setActiveByTitle(invoiceType, '增值税专用发票')
setActiveByTitle(invoiceHeader, '单位')
setActiveByTitle(goodsType, '商品明细')
} else {
setActiveByTitle(invoiceType, '电子普通发票')
props.res.receiptContent == '商品类别'
? setActiveByTitle(goodsType, '商品类别')
: setActiveByTitle(goodsType, '商品明细')
props.res.taxpayerId
? setActiveByTitle(invoiceHeader, '单位')
: setActiveByTitle(invoiceHeader, '个人')
}
syncReceiptTitle()
} else {
setActiveByTitle(invoiceType, '电子普通发票')
setActiveByTitle(invoiceHeader, '个人')
setActiveByTitle(goodsType, '商品明细')
syncReceiptTitle()
}
})
function normalizeReceiptType(type: string) {
return type === '2' || type === 'VATOSPECIAL' ? '2' : '1'
}
function getActiveTitle(list: InvoiceOption[]) {
const current = list.find((item) => item.active)
return current ? current.title : ''
}
function setActiveByTitle(list: InvoiceOption[], activeTitle: string) {
list.forEach((item) => {
item.active = item.title === activeTitle
})
}
function isUnitTitle() {
return isSpecialInvoice.value || title.value === '单位'
}
function syncReceiptTitle() {
submitData.receiptTitle = isUnitTitle()
? submitData.companyName
: submitData.personalName
}
function clearInvoiceInfo() {
submitData.receiptTitle = ''
submitData.personalName = ''
submitData.companyName = ''
submitData.taxpayerId = ''
submitData.receiptContent = ''
submitData.companyAddress = ''
submitData.companyPhone = ''
submitData.bankName = ''
submitData.bankAccount = ''
submitData.receiptPhone = ''
submitData.receiptEmail = ''
}
function handleClickHeader(
val: InvoiceOption,
_index: number,
arr: InvoiceOption[]
) {
if (val.disabled) {
return
}
const previousTitle = getActiveTitle(arr)
if (arr === invoiceType && previousTitle !== val.title) {
shouldClearOnTypeChange.value = true
}
arr.forEach((item) => {
item.active = false
})
val.active = true
}
function close(val: SubmitData | boolean) {
emit('callbackInvoice', val)
}
function submitInvoice() {
const {
receiptTitle,
taxpayerId,
companyAddress,
companyPhone,
bankName,
bankAccount,
receiptPhone,
receiptEmail,
} = submitData
syncReceiptTitle()
if (proxy.$u.test.isEmpty(receiptTitle)) {
uni.showToast({
title: '请您填写发票抬头!',
duration: 2000,
icon: 'none',
})
return false
}
if (
!proxy.$u.test.isEmpty(receiptTitle) &&
proxy.$u.test.isEmpty(taxpayerId) &&
invoiceHeader[1].active == true
) {
uni.showToast({
title: '请您填写纳税人识别号!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(companyAddress)) {
uni.showToast({
title: '请您填写单位地址!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(companyPhone)) {
uni.showToast({
title: '请您填写单位电话!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(bankName)) {
uni.showToast({
title: '请您填写开户银行!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(bankAccount)) {
uni.showToast({
title: '请您填写银行账号!',
duration: 2000,
icon: 'none',
})
return false
}
if (proxy.$u.test.isEmpty(receiptPhone)) {
uni.showToast({
title: '请您填写收票人手机!',
duration: 2000,
icon: 'none',
})
return false
}
if (!proxy.$u.test.mobile(receiptPhone)) {
uni.showToast({
title: '请输入正确的收票人手机号!',
duration: 2000,
icon: 'none',
})
return false
}
if (
!proxy.$u.test.isEmpty(receiptEmail) &&
!proxy.$u.test.email(receiptEmail)
) {
uni.showToast({
title: '请输入正确的收票人邮箱!',
duration: 2000,
icon: 'none',
})
return false
}
show.value = false
close(submitData)
}
</script>
<style scoped lang="scss">
.form-item {

View File

@@ -11,7 +11,7 @@
<u-empty text="暂无订单" mode="list"
v-if="tabItem.loaded === true && tabItem.orderList.length === 0"></u-empty>
<!-- 订单列表 -->
<view class="seller-view" :key="oderIndex" v-for="(order, oderIndex) in tabItem.orderList">
<view class="seller-view" :key="orderIndex" v-for="(order, orderIndex) in tabItem.orderList">
<!-- 店铺名称 -->
<view class="seller-info u-flex u-row-between">
<view class="seller-name wes" @click="navigateToStore(order)">
@@ -108,458 +108,339 @@
</view>
</template>
<script>
import uniLoadMore from "@/components/uni-load-more/uni-load-more.vue";
import {
getOrderList,
cancelOrder,
confirmReceipt
} from "@/api/order.js";
import {
getClearReason
} from "@/api/after-sale.js";
import LiLiWXPay from "@/js_sdk/lili-pay/wx-pay.js";
export default {
components: {
uniLoadMore,
},
data() {
return {
lightColor: this.$lightColor,
tabCurrentIndex: 0, //导航栏索引
navList: [
//导航栏list
{
state: 0,
text: "全部",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 1,
text: "待付款",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 2,
text: "待发货",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 3,
text: "待收货",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 4,
text: "已完成",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 5,
text: "已取消",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
],
status: "", //接收导航栏状态
params: {
pageNumber: 1,
pageSize: 10,
tag: "ALL",
},
orderStatus: [
//订单状态
{
orderStatus: "ALL", //全部
},
{
orderStatus: "WAIT_PAY", //代付款
},
{
orderStatus: "WAIT_SHIP",
},
{
orderStatus: "WAIT_ROG", //待收货
},
{
orderStatus: "COMPLETE", //已完成
},
{
orderStatus: "CANCELLED", //已取消
},
{
orderStatus: "STAY_PICKED_UP", //待自提
},
],
cancelShow: false, //是否显示取消
orderSn: "", //ordersn
reason: "", //取消原因
cancelList: [], //取消列表
rogShow: false, //显示是否收货
};
},
<script setup lang="ts">
import { ref, reactive, computed, watch, getCurrentInstance } from 'vue'
import { onLoad, onShow, onPullDownRefresh, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue'
import { getOrderList, cancelOrder, confirmReceipt } from '@/api/order.js'
import { getClearReason } from '@/api/after-sale.js'
import LiLiWXPay from '@/js_sdk/lili-pay/wx-pay.js'
import {
unitPrice,
parseGoodsImageUrl,
tipsToLogin,
orderStatusList,
} from '@/utils/filters.js'
/**
* 跳转到个人中心
*/
onBackPress(e) {
if (e.from == "backbutton") {
uni.switchTab({
url: "/pages/tabbar/user/my",
});
return true; //阻止默认返回行为
}
},
onPullDownRefresh() {
if (this.tabCurrentIndex) {
this.initData(this.tabCurrentIndex);
} else {
this.initData(0);
}
// this.loadData(this.status);
},
onShow() {
if (this.tipsToLogin()) {
if (!this.tabCurrentIndex) {
this.initData(0);
}
}
// this.loadData(this.status);
},
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
onLoad(options) {
/**
* 修复app端点击除全部订单外的按钮进入时不加载数据的问题
* 替换onLoad下代码即可
*/
let status = Number(options.status);
this.status = status;
interface NavTabItem {
state: number
text: string
loadStatus: string
orderList: any[]
pageNumber: number
loaded?: boolean
}
this.tabCurrentIndex = status;
// if (status == 0) {
// this.loadData(status);
// }
},
const tabCurrentIndex = ref(0)
const navList = reactive<NavTabItem[]>([
{ state: 0, text: '全部', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 1, text: '待付款', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 2, text: '待发货', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 3, text: '待收货', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 4, text: '已完成', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 5, text: '已取消', loadStatus: 'more', orderList: [], pageNumber: 1 },
])
watch: {
/**监听更改请求数据 */
tabCurrentIndex(val) {
this.params.tag = this.orderStatus[val].orderStatus;
//切换标签页将所有的页数都重置为1
this.navList.forEach((res) => {
res.pageNumber = 1;
res.loadStatus = "more";
res.orderList = [];
});
this.loadData(val);
},
},
methods: {
// 售后
applyService(order) {
uni.navigateTo({
url: `/pages/order/afterSales/afterSales?orderSn=${order.sn}`,
});
},
const status = ref<number>(0)
const params = reactive({
pageNumber: 1,
pageSize: 10,
tag: 'ALL',
})
// 店铺详情
navigateToStore(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
renderOrderTag(orderPromotionType) {
switch (orderPromotionType) {
case "NORMAL":
return "";
case "PINTUAN":
return "拼团订单";
break;
case "GIFT":
return "赠品订单";
break;
case "POINTS":
return "积分订单";
break;
case "KANJIA":
return "砍价订单";
default:
return "";
}
},
renderOrderTagType(orderPromotionType) {
switch (orderPromotionType) {
case "PINTUAN":
return "error";
case "GIFT":
return "primary";
case "POINTS":
return "info";
case "KANJIA":
return "warning";
default:
return "info";
}
},
getOrderItemImage(goods, order, index) {
let image = goods.image;
if (!image && order.groupImages) {
const images = String(order.groupImages).split(",");
image = images[index] || images[0];
}
return this.parseGoodsImageUrl(image);
},
/**
* 取消订单
*/
onCancel(sn) {
this.orderSn = sn;
this.cancelShow = true;
uni.showLoading({
title: "加载中",
});
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
this.cancelList = res.data.result;
}
if (this.$store.state.isShowToast) {
uni.hideLoading()
};
});
},
const orderStatus = [
{ orderStatus: 'ALL' },
{ orderStatus: 'WAIT_PAY' },
{ orderStatus: 'WAIT_SHIP' },
{ orderStatus: 'WAIT_ROG' },
{ orderStatus: 'COMPLETE' },
{ orderStatus: 'CANCELLED' },
{ orderStatus: 'STAY_PICKED_UP' },
]
/**
* 初始化数据
*/
initData(index) {
this.navList[index].pageNumber = 1;
this.navList[index].loadStatus = "more";
this.navList[index].orderList = [];
this.loadData(index);
},
const cancelShow = ref(false)
const orderSn = ref('')
const reason = ref('')
const cancelList = ref<any[]>([])
const rogShow = ref(false)
/**
* 等待支付
*/
waitPay(val) {
this.$u.debounce(this.pay(val), 3000);
},
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
/**
* 支付
*/
pay(val) {
if (val.sn) {
// #ifdef MP-WEIXIN
new LiLiWXPay({
sn: val.sn,
price: val.flowPrice,
orderType: "ORDER",
}).pay();
// #endif
// #ifndef MP-WEIXIN
uni.navigateTo({
url: "/pages/cart/payment/payOrder?order_sn=" + val.sn,
});
// #endif
}
},
onBackPress((e) => {
if (e.from == 'backbutton') {
uni.switchTab({ url: '/pages/tabbar/user/my' })
return true
}
return false
})
/**
* 获取订单列表
*/
loadData(index) {
this.params.pageNumber = this.navList[index].pageNumber;
// this.params.tag = this.orderStatus[index].orderStatus;
getOrderList(this.params).then((res) => {
uni.stopPullDownRefresh();
if (!res.data.success) {
this.navList[index].loadStatus = "noMore";
return false;
}
let orderList = res.data.result.records;
if (orderList.length == 0) {
this.navList[index].loadStatus = "noMore";
} else if (orderList.length < 10) {
this.navList[index].loadStatus = "noMore";
}
if (orderList.length > 0) {
this.navList[index].orderList =
this.navList[index].orderList.concat(orderList);
this.navList[index].pageNumber += 1;
}
});
},
//swiper 切换监听
changeTab(e) {
this.tabCurrentIndex = e.target.current;
},
//顶部tab点击
tabClick(index) {
this.tabCurrentIndex = index;
},
//删除订单
deleteOrder(index) {
uni.showLoading({
title: "请稍后",
});
setTimeout(() => {
this.navList[this.tabCurrentIndex].orderList.splice(index, 1);
if (this.$store.state.isShowToast) {
uni.hideLoading()
};
}, 600);
},
//取消订单
cancelOrder(item) {
uni.showLoading({
title: "请稍后",
});
setTimeout(() => {
let {
stateTip,
stateTipColor
} = this.orderStateExp(9);
item = Object.assign(item, {
state: 9,
stateTip,
stateTipColor,
});
onPullDownRefresh(() => {
if (tabCurrentIndex.value) {
initData(tabCurrentIndex.value)
} else {
initData(0)
}
})
//取消订单后删除待付款中该项
let list = this.navList[1].orderList;
let index = list.findIndex((val) => val.id === item.id);
index !== -1 && list.splice(index, 1);
if (this.$store.state.isShowToast) {
uni.hideLoading()
};
}, 600);
},
onShow(() => {
if (tipsToLogin()) {
if (!tabCurrentIndex.value) {
initData(0)
}
}
})
//订单状态文字和颜色
orderStateExp(state) {
let stateTip = "",
stateTipColor = this.$lightColor;
switch (+state) {
case 1:
stateTip = "待付款";
break;
case 2:
stateTip = "待发货";
break;
case 9:
stateTip = "订单已关闭";
stateTipColor = "#909399";
break;
onLoad((options) => {
const statusNum = Number(options?.status)
status.value = statusNum
tabCurrentIndex.value = statusNum
})
//更多自定义
}
return {
stateTip,
stateTipColor,
};
},
watch(tabCurrentIndex, (val) => {
params.tag = orderStatus[val].orderStatus
navList.forEach((res) => {
res.pageNumber = 1
res.loadStatus = 'more'
res.orderList = []
})
loadData(val)
})
/**
* 跳转到订单详情
*/
navigateToOrderDetail(sn) {
uni.navigateTo({
url: "./orderDetail?sn=" + sn,
});
},
function applyService(order: any) {
uni.navigateTo({
url: `/pages/order/afterSales/afterSales?orderSn=${order.sn}`,
})
}
/**
* 选择取消原因
*/
reasonChange(reason) {
this.reason = reason;
},
function navigateToStore(val: any) {
uni.navigateTo({
url: '/pages/product/shopPage?id=' + val.storeId,
})
}
/**
* 提交取消订单(未付款)
*/
submitCancel() {
cancelOrder(this.orderSn, {
reason: this.reason
}).then((res) => {
if (res.data.success) {
uni.showToast({
title: "订单已取消",
duration: 2000,
icon: "none",
});
this.initData(this.tabCurrentIndex);
function renderOrderTag(orderPromotionType: string) {
switch (orderPromotionType) {
case 'NORMAL':
return ''
case 'PINTUAN':
return '拼团订单'
case 'GIFT':
return '赠品订单'
case 'POINTS':
return '积分订单'
case 'KANJIA':
return '砍价订单'
default:
return ''
}
}
this.cancelShow = false;
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.cancelShow = false;
}
});
},
function renderOrderTagType(orderPromotionType: string) {
switch (orderPromotionType) {
case 'PINTUAN':
return 'error'
case 'GIFT':
return 'primary'
case 'POINTS':
return 'info'
case 'KANJIA':
return 'warning'
default:
return 'info'
}
}
/**
* 确认收货显示
*/
onRog(sn) {
this.orderSn = sn;
this.rogShow = true;
},
function getOrderItemImage(goods: any, order: any, index: number) {
let image = goods.image
if (!image && order.groupImages) {
const images = String(order.groupImages).split(',')
image = images[index] || images[0]
}
return parseGoodsImageUrl(image)
}
/**
* 点击确认收货
*/
confirmRog() {
confirmReceipt(this.orderSn).then((res) => {
if (res.data.code == 200) {
uni.showToast({
title: "已确认收货",
duration: 2000,
icon: "none",
});
this.initData(this.tabCurrentIndex);
this.rogShow = false;
}
});
},
function onCancel(sn: string) {
orderSn.value = sn
cancelShow.value = true
uni.showLoading({ title: '加载中' })
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
cancelList.value = res.data.result
}
hideLoadingIfNeeded()
})
}
/**
* 评价商品
*/
onComment(sn) {
uni.navigateTo({
url: "./evaluate/myEvaluate",
});
},
function initData(index: number) {
navList[index].pageNumber = 1
navList[index].loadStatus = 'more'
navList[index].orderList = []
loadData(index)
}
/**
* 重新购买
*/
reBuy(order) {
console.log(order);
return;
uni.navigateTo({
url: "/pages/product/goods?id=" + order.id + "&goodsId=" + order.goodsId,
});
},
},
};
function waitPay(val: any) {
proxy?.$u?.debounce?.(() => pay(val), 3000, true)?.()
}
function pay(val: any) {
if (val.sn) {
// #ifdef MP-WEIXIN
new LiLiWXPay({
sn: val.sn,
price: val.flowPrice,
orderType: 'ORDER',
}).pay()
// #endif
// #ifndef MP-WEIXIN
uni.navigateTo({
url: '/pages/cart/payment/payOrder?order_sn=' + val.sn,
})
// #endif
}
}
function loadData(index: number) {
params.pageNumber = navList[index].pageNumber
getOrderList(params).then((res) => {
uni.stopPullDownRefresh()
if (!res.data.success) {
navList[index].loadStatus = 'noMore'
return false
}
const records = res.data.result.records
if (records.length == 0) {
navList[index].loadStatus = 'noMore'
} else if (records.length < 10) {
navList[index].loadStatus = 'noMore'
}
if (records.length > 0) {
navList[index].orderList = navList[index].orderList.concat(records)
navList[index].pageNumber += 1
}
})
}
function changeTab(e: any) {
tabCurrentIndex.value = e.target.current
}
function tabClick(index: number) {
tabCurrentIndex.value = index
}
function deleteOrder(index: number) {
uni.showLoading({ title: '请稍后' })
setTimeout(() => {
navList[tabCurrentIndex.value].orderList.splice(index, 1)
hideLoadingIfNeeded()
}, 600)
}
function cancelOrderLocal(item: any) {
uni.showLoading({ title: '请稍后' })
setTimeout(() => {
const { stateTip, stateTipColor } = orderStateExp(9)
Object.assign(item, {
state: 9,
stateTip,
stateTipColor,
})
const list = navList[1].orderList
const idx = list.findIndex((val) => val.id === item.id)
if (idx !== -1) list.splice(idx, 1)
hideLoadingIfNeeded()
}, 600)
}
function orderStateExp(state: number) {
let stateTip = ''
let stateTipColor = lightColor.value
switch (+state) {
case 1:
stateTip = '待付款'
break
case 2:
stateTip = '待发货'
break
case 9:
stateTip = '订单已关闭'
stateTipColor = '#909399'
break
}
return { stateTip, stateTipColor }
}
function navigateToOrderDetail(sn: string) {
uni.navigateTo({
url: './orderDetail?sn=' + sn,
})
}
function reasonChange(val: string) {
reason.value = val
}
function submitCancel() {
cancelOrder(orderSn.value, { reason: reason.value }).then((res) => {
if (res.data.success) {
uni.showToast({
title: '订单已取消',
duration: 2000,
icon: 'none',
})
initData(tabCurrentIndex.value)
cancelShow.value = false
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
cancelShow.value = false
}
})
}
function onRog(sn: string) {
orderSn.value = sn
rogShow.value = true
}
function confirmRog() {
confirmReceipt(orderSn.value).then((res) => {
if (res.data.code == 200) {
uni.showToast({
title: '已确认收货',
duration: 2000,
icon: 'none',
})
initData(tabCurrentIndex.value)
rogShow.value = false
}
})
}
function onComment(_sn: string) {
uni.navigateTo({
url: './evaluate/myEvaluate',
})
}
function reBuy(order: any) {
const goods = order.orderItems?.[0]
if (!goods) return
uni.navigateTo({
url: '/pages/product/goods?id=' + goods.id + '&goodsId=' + goods.goodsId,
})
}
</script>
<style lang="scss" scoped>

View File

@@ -51,7 +51,7 @@
<view class="title">自提点地址:</view>
<view class="value address-line-height">{{ order.storeAddressPath }}</view>
</view>
<view class="order-info-view" @click="callPhone" >
<view class="order-info-view" @click="handleCallPhone" >
<view class="title">联系方式:</view>
<view class="value">{{ order.storeAddressMobile }}<u-icon name='phone-fill' ></u-icon></view>
</view>
@@ -145,7 +145,9 @@
<view class="title">订单编号</view>
<view class="value order-sn-value">
<text class="order-sn-text">{{ order.sn }}</text>
<u-tag class="copy" text="复制" type="info" mode="plain" size="mini" @click="onCopy(order.sn)" />
<view class="copy" @click.stop="onCopy(order.sn)">
<u-tag text="复制" type="info" plain size="mini" />
</view>
</view>
</view>
<view class="order-info-view">
@@ -245,287 +247,262 @@
</view>
</template>
<script>
import { getExpress, getPackage } from "@/api/trade.js";
import { cancelOrder, confirmReceipt, getOrderDetail } from "@/api/order.js";
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getExpress, getPackage } from '@/api/trade.js'
import { cancelOrder, confirmReceipt, getOrderDetail } from '@/api/order.js'
import shares from '@/components/m-share/index'
import { getClearReason } from '@/api/after-sale.js'
import {
unitPrice,
secrecyMobile,
setClipboard,
talkIm,
callPhone,
} from '@/utils/filters.js'
import shares from "@/components/m-share/index"; //分享
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
import { getClearReason } from "@/api/after-sale.js";
const orderStatusMap: Record<string, { title: string; value?: string }> = {
UNPAID: { title: '未付款', value: '商品暂未付款' },
PAID: { title: '已付款', value: '买家已付款' },
UNDELIVERED: { title: '待发货', value: '商品等待发货中' },
PARTS_DELIVERED: { title: '部分发货', value: '商品已部分发货。' },
DELIVERED: { title: '已发货', value: '商品已发货,请您耐心等待' },
CANCELLED: { title: '已取消', value: '订单已取消' },
COMPLETED: { title: '已完成', value: '订单已完成,祝您生活愉快' },
STAY_PICKED_UP: { title: '待自提', value: '商品正在等待提取' },
TAKE: { title: '待核验' },
}
export default {
components: {
shares,
},
data() {
return {
lightColor: this.$lightColor,
logisticsList: "", //物流信息
shareFlag: false, //拼团分享开关
orderStatusMap: {
UNPAID: {
title: "未付款",
value: "商品暂未付款",
},
PAID: {
title: "已付款",
value: "买家已付款",
},
UNDELIVERED: {
title: "待发货",
value: "商品等待发货中",
},
PARTS_DELIVERED: {
title: "部分发货",
value: "商品已部分发货。",
},
DELIVERED: {
title: "已发货",
value: "商品已发货,请您耐心等待",
},
CANCELLED: {
title: "已取消",
value: "订单已取消",
},
COMPLETED: {
title: "已完成",
value: "订单已完成,祝您生活愉快",
},
STAY_PICKED_UP: {
title: "待自提",
value: "商品正在等待提取",
},
TAKE: {
title: "待核验",
},
},
order: {},
cancelShow: false, //取消订单
orderSn: "",
orderGoodsList: "", //订单中商品集合
orderDetail: "", //订单详情信息
sn: "",
cancelList: "",
rogShow: false,
reason: "",
orderPackage:"",
};
},
onLoad(options) {
this.loadData(options.sn);
this.sn = options.sn;
},
methods: {
//获取包裹
async getOrderPackage() {
getPackage(this.order.sn).then(res => {
if (res.data.success) {
this.orderPackage = res.data.result
}
})
},
handleClickDeliver(){
uni.navigateTo({
url: `/pages/order/deliverDetail?order_sn=${this.order.sn}`,
});
},
// 退款状态枚举
refundPriceList(status) {
switch (status) {
case 'ALL_REFUND':
return "全部退款";
case 'PART_REFUND':
return "部分退款";
case 'NO_REFUND':
return "未退款";
case 'REFUNDING':
return "退款中";
default:
return "";
const logisticsList = ref<any>('')
const shareFlag = ref(false)
const order = ref<Record<string, any>>({})
const cancelShow = ref(false)
const orderSn = ref('')
const orderGoodsList = ref<any[]>([])
const orderDetail = ref<Record<string, any>>({})
const sn = ref('')
const cancelList = ref<any[]>([])
const rogShow = ref(false)
const reason = ref('')
const orderPackage = ref<any>('')
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
onLoad((options) => {
const orderSnParam = options?.sn || ''
sn.value = orderSnParam
loadData(orderSnParam)
})
function getOrderPackage() {
getPackage(order.value.sn).then((res) => {
if (res.data.success) {
orderPackage.value = res.data.result
}
},
callPhone(){
this.callPhone(this.order.storeAddressMobile )
},
//联系客服
contact(storeId){
this.talkIm(storeId)
},
goToShopPage(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
// 获取物流信息
loadLogistics(sn) {
getExpress(sn).then((res) => {
this.logisticsList = res.data.result;
});
},
})
}
// 分享当前拼团信息
inviteGroup() {
this.shareFlag = true;
},
// #TODO 这块需要写一下 目前没有拼团的详细信息
ByUserMessage(order) {
uni.navigateTo({
url:
"/pages/cart/payment/shareOrderGoods?sn=" +
order.sn +
"&sku=" +
this.orderGoodsList[0].skuId +
"&goodsId=" +
this.orderGoodsList[0].goodsId,
});
},
async loadData(sn) {
uni.showLoading({
title: "加载中",
});
getOrderDetail(sn).then((res) => {
const order = res.data.result;
this.order = order.order;
this.orderGoodsList = order.orderItems;
this.orderDetail = res.data.result;
if (this.order.deliveryMethod === 'LOGISTICS') {
this.loadLogistics(sn);
this.getOrderPackage();
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
function handleClickDeliver() {
uni.navigateTo({
url: `/pages/order/deliverDetail?order_sn=${order.value.sn}`,
})
}
},
onReceipt(val) {
uni.navigateTo({
url: "/pages/order/invoice/invoiceDetail?id=" + val.id,
});
},
gotoGoodsDetail(sku) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId}`,
});
},
onCopy(sn) {
setClipboard(sn)
},
function refundPriceList(status: string) {
switch (status) {
case 'ALL_REFUND':
return '全部退款'
case 'PART_REFUND':
return '部分退款'
case 'NO_REFUND':
return '未退款'
case 'REFUNDING':
return '退款中'
default:
return ''
}
}
//售后按钮
onAfterSales(sn, sku) {
uni.navigateTo({
url: `./afterSales/afterSalesSelect?sn=${sn}&sku=${encodeURIComponent(
JSON.stringify(sku)
)}`,
});
},
// 去支付
toPay(val) {
val.sn
? uni.navigateTo({
url: "/pages/cart/payment/payOrder?order_sn=" + val.sn,
})
: false;
}, //删除订单
deleteOrder(index) {
uni.showLoading({
title: "请稍后",
});
setTimeout(() => {
this.navList[this.tabCurrentIndex].orderList.splice(index, 1);
if (this.$store.state.isShowToast){ uni.hideLoading() };
}, 600);
},
//取消订单
onCancel(sn) {
this.orderSn = sn;
function handleCallPhone() {
if (order.value.storeAddressMobile) {
callPhone(order.value.storeAddressMobile)
}
}
uni.showLoading({
title: "加载中",
});
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
this.cancelList = res.data.result;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
function contact(storeId: string) {
talkIm(storeId)
}
this.cancelShow = true;
},
function goToShopPage(val: any) {
uni.navigateTo({
url: '/pages/product/shopPage?id=' + val.storeId,
})
}
//提交取消订单(未付款)
submitCancel() {
cancelOrder(this.orderSn, { reason: this.reason }).then((res) => {
if (res.data.success) {
uni.showToast({
title: "已取消",
duration: 2000,
icon: "none",
});
this.cancelShow = false;
setTimeout(() => {
uni.reLaunch({
url: "/pages/order/myOrder?status=0",
});
}, 500);
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.cancelShow = false;
}
});
},
function loadLogistics(orderSnParam: string) {
getExpress(orderSnParam).then((res) => {
logisticsList.value = res.data.result
})
}
//确认收货
onRog(sn) {
this.orderSn = sn;
this.rogShow = true;
},
confirmRog() {
confirmReceipt(this.orderSn).then((res) => {
if (res.data.success) {
uni.showToast({
title: "已确认收货",
duration: 2000,
icon: "none",
});
this.rogShow = false;
this.loadData(this.sn);
}
});
},
//评价商品
onComment(sn) {
uni.navigateTo({
url: "./evaluate/myEvaluate",
});
}, //查看物流
onLogistics(order) {
uni.navigateTo({
url:
"/pages/mine/msgTips/packageMsg/logisticsDetail?logi_id=" +
order.logi_id +
"&ship_no=" +
order.ship_no +
"&order_sn=" +
order.sn,
});
},
function inviteGroup() {
shareFlag.value = true
}
//选择取消原因
reasonChange(reason) {
this.reason = reason;
},
reBuy(order) {
uni.navigateTo({
url:
"/pages/product/goods?id=" + order.id + "&goodsId=" + order.goodsId,
});
},
},
};
function ByUserMessage(orderItem: any) {
uni.navigateTo({
url:
'/pages/cart/payment/shareOrderGoods?sn=' +
orderItem.sn +
'&sku=' +
orderGoodsList.value[0].skuId +
'&goodsId=' +
orderGoodsList.value[0].goodsId,
})
}
function loadData(orderSnParam: string) {
uni.showLoading({ title: '加载中' })
getOrderDetail(orderSnParam).then((res) => {
const result = res.data.result
order.value = result.order
orderGoodsList.value = result.orderItems
orderDetail.value = result
if (order.value.deliveryMethod === 'LOGISTICS') {
loadLogistics(orderSnParam)
getOrderPackage()
}
hideLoadingIfNeeded()
})
}
function onReceipt(val: any) {
uni.navigateTo({
url: '/pages/order/invoice/invoiceDetail?id=' + val.id,
})
}
function gotoGoodsDetail(sku: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId}`,
})
}
function onCopy(orderSnText: string) {
if (!orderSnText) {
uni.showToast({ title: '暂无订单编号', icon: 'none' })
return
}
setClipboard(String(orderSnText))
}
function onAfterSales(orderSnText: string, sku: any) {
uni.navigateTo({
url: `./afterSales/afterSalesSelect?sn=${orderSnText}&sku=${encodeURIComponent(
JSON.stringify(sku)
)}`,
})
}
function toPay(val: any) {
if (val.sn) {
uni.navigateTo({
url: '/pages/cart/payment/payOrder?order_sn=' + val.sn,
})
}
}
function onCancel(orderSnText: string) {
orderSn.value = orderSnText
uni.showLoading({ title: '加载中' })
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
cancelList.value = res.data.result
}
hideLoadingIfNeeded()
})
cancelShow.value = true
}
function submitCancel() {
cancelOrder(orderSn.value, { reason: reason.value }).then((res) => {
if (res.data.success) {
uni.showToast({
title: '已取消',
duration: 2000,
icon: 'none',
})
cancelShow.value = false
setTimeout(() => {
uni.reLaunch({
url: '/pages/order/myOrder?status=0',
})
}, 500)
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
cancelShow.value = false
}
})
}
function onRog(orderSnText: string) {
orderSn.value = orderSnText
rogShow.value = true
}
function confirmRog() {
confirmReceipt(orderSn.value).then((res) => {
if (res.data.success) {
uni.showToast({
title: '已确认收货',
duration: 2000,
icon: 'none',
})
rogShow.value = false
loadData(sn.value)
}
})
}
function onComment(_orderSnText: string) {
uni.navigateTo({
url: './evaluate/myEvaluate',
})
}
function onLogistics(orderItem: any) {
uni.navigateTo({
url:
'/pages/mine/msgTips/packageMsg/logisticsDetail?logi_id=' +
orderItem.logi_id +
'&ship_no=' +
orderItem.ship_no +
'&order_sn=' +
orderItem.sn,
})
}
function reasonChange(val: string) {
reason.value = val
}
function reBuy(orderItem: any) {
uni.navigateTo({
url: '/pages/product/goods?id=' + orderItem.id + '&goodsId=' + orderItem.goodsId,
})
}
</script>
<style lang="scss">

View File

@@ -10,31 +10,23 @@
</view>
</template>
<script>
import { getArticleDetail } from "@/api/article.js";
export default {
data() {
return {
// 用于接收上一级通过路径传输的数据
routers: "",
// 请求文章接口后存储文章信息
articleData: "",
};
},
onLoad(val) {
this.routers = val;
getArticleDetail(val.id).then((res) => {
if (res.data.result) {
// 将请求的文章数据赋值
this.articleData = res.data.result.content;
}
// 修改当前NavigationBar(标题头)为文章头部
uni.setNavigationBarTitle({
title: val.title,
});
});
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getArticleDetail } from '@/api/article.js'
const articleData = ref('')
onLoad((val) => {
getArticleDetail(val.id).then((res) => {
if (res.data.result) {
articleData.value = res.data.result.content
}
uni.setNavigationBarTitle({
title: val.title,
})
})
})
</script>
<style lang="scss" scoped>
page {

View File

@@ -1,5 +1,5 @@
<template>
<div>
<div class="seller-control" :style="themeStyle">
<u-navbar
:border="false"
:fixed="true"
@@ -18,56 +18,71 @@
</div>
</template>
<script>
import { getCompanyDetail } from "@/api/entry";
import step1 from "./step1";
import step2 from "./step2";
import step3 from "./step3";
export default {
data() {
return {
companyData: "",
current: 1,
};
},
components: {
step1,
step2,
step3,
},
mounted() {
this.init();
},
methods: {
back() {
if (this.current > 1) {
this.current--;
return;
}
uni.navigateBack({
delta: 1,
fail: () => {
uni.switchTab({ url: "/pages/tabbar/home/index" });
},
});
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useStore } from '@/store'
import { getCompanyDetail } from '@/api/entry'
import step1 from './step1.vue'
import step2 from './step2.vue'
import step3 from './step3.vue'
import { getThemeStyle } from '@/utils/theme'
const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const companyData = ref<any>('')
const current = ref(1)
onMounted(() => {
init()
})
function back() {
if (current.value > 1) {
current.value--
return
}
uni.navigateBack({
delta: 1,
fail: () => {
uni.switchTab({ url: '/pages/tabbar/home/index' })
},
async init(next) {
const res = await getCompanyDetail();
if (res.data.success) {
this.companyData = res.data.result;
next ? this.current++ : "";
}
},
next() {
this.init("next");
},
finished() {
uni.navigateTo({
url: "/pages/passport/entry/seller/index",
});
},
},
};
})
}
async function init(next?: string) {
const res = await getCompanyDetail()
if (res.data.success) {
companyData.value = res.data.result
if (next) {
current.value++
}
}
}
function next() {
init('next')
}
function finished() {
uni.navigateTo({
url: '/pages/passport/entry/seller/index',
})
}
</script>
<style lang="scss" scoped></style>
<style lang="scss">
page {
background: #f7f7f7;
}
</style>
<style lang="scss" scoped>
@import "./entry.scss";
.seller-control {
min-height: 100vh;
background: #f7f7f7;
}
</style>

Some files were not shown because too many files have changed in this diff Show More