refactor: 重构多个组件以支持 Vue 3 语法和功能

- 将多个组件转换为 `<script setup>` 语法,提升可读性和性能
- 优化状态管理和事件处理逻辑,简化代码结构
- 更新样式和布局以适应新组件结构
- 添加新功能和修复已知问题,提升用户体验
This commit is contained in:
Yer11214
2026-07-08 18:37:11 +08:00
parent 4d0b0fb5e4
commit 349ffa4f34
189 changed files with 18278 additions and 20758 deletions

403
App.vue
View File

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

View File

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

View File

@@ -13,72 +13,62 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import config from "@/config/config"; import { ref, onMounted } from 'vue'
import logoImg from "@/icon.png"; import config from "@/config/config"
export default { import logoImg from "@/icon.png"
data() {
return { const weChat = ref(false)
config, // 设置工具类 const logo = logoImg
weChat: false, // 是否微信浏览器该项为true时不显示 当前整个页面
logo: logoImg, //显示的圆形logo onMounted(() => {
}; // #ifdef H5
}, // 判断是否是微信浏览器
mounted() { var ua = navigator.userAgent.toLowerCase();
// #ifdef H5 var isWeixin = ua.indexOf("micromessenger") != -1;
// 判断是否是微信浏览器 weChat.value = isWeixin ? true : false
var ua = navigator.userAgent.toLowerCase(); // #endif
var isWeixin = ua.indexOf("micromessenger") != -1; })
if (isWeixin) {
this.weChat = true; /**
* 跳转到下载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 { } else {
this.weChat = false; var r = document.createElement("iframe");
(r.src = t), (r.style.display = "none"), document.body.appendChild(r)
downloadApp()
} }
// #endif } catch (e) {
}, window.location.href = t
methods: { downloadApp()
}
/** }
* 跳转到下载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();
}
},
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -82,19 +82,19 @@
<!-- 拼团购买仅筛选出当前拼团类型商品 --> <!-- 拼团购买仅筛选出当前拼团类型商品 -->
<template v-for="(spec_val, spec_index) in spec.values" :key="spec_index"> <template v-for="(spec_val, spec_index) in spec.values" :key="spec_index">
<view <view
v-if="parentOrder && spec_val.skuId == goodsDetail.id" v-if="parentOrder && spec_val.skuId == goodsDetail.id"
:class="{ active: spec_val.value == currentSelected[specIndex] }" :class="{ active: spec_val.value == currentSelected[specIndex] }"
class="skus-view-item" class="skus-view-item"
@click="handleClickSpec(spec, specIndex, spec_val)" @click="handleClickSpec(spec, specIndex, spec_val)"
> >
{{ spec_val.value }} {{ spec_val.value }}
</view> </view>
</template> </template>
</view> </view>
</view> </view>
<div class="soldout" v-if="goodsDetail.quantity === 0"> <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> </div>
<!-- 数量 --> <!-- 数量 -->
<view v-if="goodsDetail.quantity !== 0" class="goods-skus-number flex flex-a-c flex-j-sb"> <view v-if="goodsDetail.quantity !== 0" class="goods-skus-number flex flex-a-c flex-j-sb">
@@ -111,313 +111,269 @@
</u-popup> </u-popup>
</div> </div>
</template> </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: '', //选中商品的昵称 <script setup lang="ts">
selectSkuList: '', //选中商铺sku, import { ref, reactive, watch, onMounted } from 'vue'
selectedSpecImg: '', //选中的图片路径 import * as API_trade from '@/api/trade.js'
buyType: '', //用于存储促销,拼团等活动类型 import setup from './popup.js'
parentOrder: '', //父级拼团活动的数据 - 如果是团员则有数据 import uniNumberBox from '@/components/uni-number-box.vue'
formatList: [], import { goodsFormatPrice } from '@/utils/filters.js'
currentSelected: [],
skuList: '', const props = withDefaults(defineProps<{
isClose: false //是否可以点击遮罩关闭 wholesaleList?: any[] | boolean
}; buyMask?: boolean
}, isGroup?: boolean
props: { goodsDetail?: any
wholesaleList: { selectedSku?: any | null
type: null, goodsSpec?: any | null
default: false addr?: any | null
}, pointDetail?: any | null
buyMask: { }>(), {
type: Boolean, wholesaleList: false,
default: false buyMask: false,
}, isGroup: false,
isGroup: { goodsDetail: () => ({}),
type: Boolean, selectedSku: '',
default: false goodsSpec: '',
}, addr: '',
goodsDetail: { pointDetail: ''
default: '', })
type: null
}, const emit = defineEmits(['closeBuy', 'changed', 'handleClickSku', 'queryCart'])
selectedSku: {
default: '', const num = ref(props.wholesaleList && Array.isArray(props.wholesaleList) && props.wholesaleList.length > 0 ? props.wholesaleList[0].num : 1)
type: null
}, const selectName = ref('')
goodsSpec: { const selectSkuList = ref<any>('')
default: '', const selectedSpecImg = ref('')
type: null const buyType = ref('')
}, const parentOrder = ref<any>('')
addr: { const formatList = ref<any[]>([])
default: '', const currentSelected = ref<string[]>([])
type: null const skuList = ref('')
},
pointDetail: { watch(num, (val) => {
default: '', val == 0 ? num.value = 1 : ''
type: null 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import './popup.scss'; @import './popup.scss';

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <view>
<!-- 一行两列商品展示 --> <!-- 一行两列商品展示 -->
<view class="goods-list" v-if="type == 'twoColumns'"> <view class="goods-list" v-if="type == 'twoColumns'">
<view v-for="(item, index) in res" :key="index" class="goods-item"> <view v-for="(item, index) in res" :key="index" class="goods-item">
@@ -10,7 +10,7 @@
height="330rpx" height="330rpx"
mode="aspectFit" mode="aspectFit"
> >
<template #loading><u-loading></u-loading></template> <template #loading><u-loading-icon></u-loading-icon></template>
</u-image> </u-image>
</view> </view>
<view class="goods-detail"> <view class="goods-detail">
@@ -57,7 +57,7 @@
</view> </view>
<!-- 一行一列商品展示 --> <!-- 一行一列商品展示 -->
<div v-if="type == 'oneColumns'"> <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="flex goods-col">
<div class="goods-img" @click="navigateToDetailPage(item)"> <div class="goods-img" @click="navigateToDetailPage(item)">
<u-image <u-image
@@ -67,7 +67,7 @@
mode="aspectFit" mode="aspectFit"
:src="item.goodsImage || item.thumbnail" :src="item.goodsImage || item.thumbnail"
> >
<template #loading><u-loading></u-loading></template> <template #loading><u-loading-icon></u-loading-icon></template>
</u-image> </u-image>
</div> </div>
<div class="goods-detail"> <div class="goods-detail">
@@ -112,41 +112,26 @@
</div> </div>
</div> </div>
</div> </div>
</div> </view>
</template> </template>
<script> <script setup lang="ts">
import commonTpl from "@/components/m-goods-list/common"; import { useGoodsListCommon } from './common'
export default { import { goodsFormatPrice } from '@/utils/filters.js'
data() {
return { const { lightSearchStr, getPromotion, navigateToDetailPage, navigateToStoreDetailPage } = useGoodsListCommon()
lightColor: this.$mainColor,
}; const props = withDefaults(defineProps<{
}, type?: string
mixins: [commonTpl], res?: any[]
props: { storeName?: boolean
// 展示的类型 keyword?: string | null
type:{ }>(), {
type:String, type: 'oneColumns',
default:"oneColumns" res: () => [],
}, storeName: true,
// 遍历的数据 keyword: ''
res: { })
type: Array,
default: () => {
return [];
},
},
},
methods: {
// 跳转到商品详情
navigateToDetailPage(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
},
};
</script> </script>
<style lang="scss" scoped> <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 v-for="(item, index) in res" :key="index" class="goods-item">
<view class="image-wrapper" @click="navigateToDetailPage(item)"> <view class="image-wrapper" @click="navigateToDetailPage(item)">
<u-image :src="item.thumbnail" width="100%" height='330rpx' mode="aspectFit"> <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> </u-image>
</view> </view>
<view class="goods-detail"> <view class="goods-detail">
@@ -51,7 +51,7 @@
<div class="flex goods-col"> <div class="flex goods-col">
<div class="goods-img" @click="navigateToDetailPage(item)"> <div class="goods-img" @click="navigateToDetailPage(item)">
<u-image width="230rpx" mode="aspectFit" border-radius='16' height="230rpx" :src="item.thumbnail"> <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> </u-image>
</div> </div>
<div class="goods-detail"> <div class="goods-detail">
@@ -94,114 +94,32 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import commonTpl from '@/components/m-goods-list/common' import { watch } from 'vue'
export default { import { useGoodsListCommon } from './common'
data() { import { goodsFormatPrice } from '@/utils/filters.js'
return {
lightColor: this.$mainColor
}
},
mixins: [commonTpl],
props: { const { lightSearchStr, getPromotion, navigateToDetailPage, navigateToStoreDetailPage } = useGoodsListCommon()
// 遍历的数据
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: ''
}
}, const props = withDefaults(defineProps<{
watch: { res?: any[]
keyword(val) { type?: string
if (val) { storeName?: boolean
this.lightSearchStr(val) tabBarGap?: boolean
} keyword?: string | null
} }>(), {
}, res: () => [],
methods: { type: 'twoColumns',
storeName: true,
tabBarGap: true,
keyword: ''
})
// 高亮显示搜索内容 watch(() => props.keyword, (val) => {
lightSearchStr(keyword, str) { if (val) {
if (!keyword) { lightSearchStr(val as string, '')
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}`,
});
},
}
} }
})
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
@@ -212,6 +130,7 @@
.goods-list { .goods-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between;
margin: 10rpx 20rpx 284rpx; margin: 10rpx 20rpx 284rpx;
width: calc(100% - 40rpx); width: calc(100% - 40rpx);
box-sizing: border-box; box-sizing: border-box;
@@ -225,14 +144,10 @@
display: flex; display: flex;
border-radius: 16rpx; border-radius: 16rpx;
flex-direction: column; flex-direction: column;
width: calc(50% - 30rpx); width: calc(50% - 10rpx);
margin-bottom: 20rpx; margin-bottom: 20rpx;
padding-bottom: 20rpx; padding-bottom: 20rpx;
&:nth-child(2n + 1) {
margin-right: 20rpx;
}
.image-wrapper { .image-wrapper {
width: 100%; width: 100%;
@@ -344,7 +259,7 @@
color: $light-color; color: $light-color;
margin-right: 10rpx; margin-right: 10rpx;
padding: 0 4rpx; padding: 0 4rpx;
border-radius: 2rpx; border-radius: 2px;
} }
} }
@@ -421,6 +336,5 @@
} }
} }
} }
} }
</style> </style>

View File

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

View File

@@ -1,73 +1,63 @@
<template> <template>
<div> <view>
<div class="goods-recommend">{{title ? `--${title}-- `:''}}</div> <view class="goods-recommend">{{title ? `--${title}-- `:''}}</view>
<goodsTemplate :res='goodsList' /> <goodsTemplate :res='goodsList' />
</div> </view>
</template> </template>
<script> <script setup lang="ts">
import goodsTemplate from '@/components/m-goods-list/list' import { ref, onMounted } from 'vue'
import { getGoodsList } from "@/api/goods.js"; import goodsTemplate from '@/components/m-goods-list/list.vue'
export default { import { getGoodsList } from "@/api/goods.js"
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,
})
)
);
Object.keys(submit).map((key) => { const props = withDefaults(defineProps<{
if (!submit[key] || submit[key].length == 0) { title?: string
delete submit[key]; pageSize?: number | null
} categoryId?: string | null
}); storeId?: string | null
let goodsList = await getGoodsList(submit); }>(), {
this.goodsList.push(...goodsList.data.result.records); title: '',
}, pageSize: 12,
handleClick(item) { categoryId: '',
uni.navigateTo({ storeId: ''
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`, })
});
}, 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> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="index"> <view class="index">
<view v-model="show" class="slot-content"> <view v-model="show" class="slot-content">
<image @click="downLoad()" class="img" :src="imgUrl" /> <image @click="downLoad()" class="img" :src="imgUrl" />
<div class="canvas-hide"> <div class="canvas-hide">
@@ -11,181 +11,153 @@
<!-- #endif --> <!-- #endif -->
</div> </div>
</view> </view>
</div> </view>
</template> </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 uQRCode from '@/components/Sansnn-uQRCode/uqrcode.js'
import logoImg from "@/pages/passport/static/logo-title.png"; import logoImg from "@/pages/passport/static/logo-title.png"
import myFaceImg from "@/pages/passport/static/missing-face.png"; import myFaceImg from "@/pages/passport/static/missing-face.png"
export default { const instance = getCurrentInstance()
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
// #ifndef MP-WEIXIN const props = defineProps<{
st2: (size) => size, res?: any
// #endif }>()
/** 保存图片 */ const imgUrl = ref("")
downLoad() { const show = ref(false)
uni.saveImageToPhotosAlbum({ const dp = ref<any>({})
filePath: this.imgUrl, const logo = logoImg
success: function () { const myFace = myFaceImg
uni.showToast({title: "保存成功!",icon: "none",}); const sharingLink = ref('')
},
fail: function () { /** 解决微信小程序中图片模糊问题 */
uni.showToast({title: "保存失败,请稍后重试!",icon: "none",}); // #ifdef MP-WEIXIN
}, const st2 = (size: number) => size * 2
}); // #endif
},
/** 创建canvas */ // #ifndef MP-WEIXIN
async init() { const st2 = (size: number) => size
this.show = true; // #endif
this.dp = await DrawPoster.build({
selector: "canvas", /** 保存图片 */
componentThis: this, const downLoad = () => {
loading: true, uni.saveImageToPhotosAlbum({
debugging: true, filePath: imgUrl.value,
}); success: function () {
let dp = this.dp; uni.showToast({title: "保存成功!",icon: "none"})
// #ifdef MP-WEIXIN },
// 用于微信小程序中画布错乱问题 fail: function () {
dp.canvas.width = this.st2(560); uni.showToast({title: "保存失败,请稍后重试!",icon: "none"})
dp.canvas.height = this.st2(800); },
// #endif })
this.showQRCode(dp); }
},
/** 生成二维码 */ /** 创建canvas */
async showQRCode(dp){ const init = async () => {
await uQRCode.make({ show.value = true
canvasId: 'canvas', // canvas画布 dp.value = await DrawPoster.build({
componentInstance: this, selector: "canvas",
text: this.res.bottom.code, // 二维码内容 componentThis: instance?.proxy,
size: 130, // 二维码大小 loading: true,
margin: 5, // 二维码内边距 debugging: true,
backgroundColor: '#ffffff', // 二维码背景颜色 })
foregroundColor: '#000000', // !二维码色块颜色 let dpVal = dp.value
fileType: 'jpg', // #ifdef MP-WEIXIN
errorCorrectLevel: 0, // <== 关键 容错率M:0,L:1,H:2,Q:3, // 用于微信小程序中画布错乱问题
// errorCorrectLevel: uQRCode.errorCorrectLevel.M, // <== 关键 容错率M:0,L:1,H:2,Q:3, dpVal.canvas.width = st2(560)
success: res => { dpVal.canvas.height = st2(800)
this.sharingLink = res; // res => 图片路径 // #endif
// 对画布进行绘制 await showQRCode(dpVal)
this.draw(dp); }
},
fail: res => { /** 生成二维码 */
// console.log('失败',res) 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,
}) })
}, ctx.fillStyle = "#ff3c2a"
async draw(dp) { ctx.font = `${st2(38)}px PingFang SC`
const { face, nickName, desc } = this.res.memberInfo; ctx.textAlign = "left"
const { width, height, background, title } = this.res.container; ctx.fillText(price, st2(60), st2(665))
const { code, img, price } = this.res.bottom; })
/** 绘制背景 */ // 绘制生成本地地址
await dp.draw((ctx) => { imgUrl.value = await dp.createImagePath()
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));
// });
// 绘制生成本地地址 onMounted(() => {
this.imgUrl = await dp.createImagePath(); init()
}, })
},
async mounted() {
this.init();
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -28,138 +28,122 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
export default { import { ref, watch, onMounted } from 'vue'
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修改 清空内容时候不进行搜索
},
/** const props = withDefaults(defineProps<{
* 回退到上一级 mode?: number
*/ placeholder?: string
onClickLeft() { value?: string
const paths = getCurrentPages(); radius?: string
console.log(paths) isFocusVal?: boolean
if(paths.length > 1){ showClear?: boolean
uni.navigateBack(); }>(), {
}else{ mode: 1,
uni.switchTab({ placeholder: "请输入搜索内容",
url:"/pages/tabbar/home/index" value: "",
}) radius: "60",
} isFocusVal: true,
}, showClear: true,
})
/** const emit = defineEmits(['confirm', 'input', 'search', 'SwitchType'])
* 内容为空时,输入默认关键字
*/ const isShowSeachGoods = ref(false)
search() { const active = ref(false)
if (!this.inputVal) { const inputVal = ref("")
if (this.searchName == "取消") { const isDelShow = ref(false)
uni.hideKeyboard(); const isFocus = ref(false)
this.isFocus = false; const switchLayout = ref(true)
this.active = false;
return; onMounted(() => {
} isFocus.value = props.isFocusVal
} })
this.$emit("search", this.inputVal ? this.inputVal : this.placeholder);
}, //
}, const out = () => {
watch: { uni.reLaunch({
/** url: "/pages/tabbar/home/index",
* 监听当前是否有值 是否显示清除图标 })
*/ }
inputVal(newVal) { // 切换排列顺序
newVal ? (this.isDelShow = true) : (this.isDelShow = false); 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)
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
//HM修改 清空内容时候触发组件input
emit("input", "")
//this.$emit('search', '');//HM修改 清空内容时候不进行搜索
}
/**
* 回退到上一级
*/
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)
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -28,118 +28,125 @@
</view> </view>
</u-popup> </u-popup>
</template> </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 { <script setup lang="ts">
mixins: [mpShare], import { ref } from 'vue'
data() { import configs from "@/config/config"
return { import mpShare from "@/utils/mpShare.js"
configs, import { setClipboard } from "@/utils/filters.js"
show: true,
list: [ // mixin 处理mpShare 提供小程序分享能力
{ mpShare
color: "#04BE02",
title: "微信好友", const props = defineProps<{
icon: "weixin-fill", thumbnail?: string
type: 0, goodsName?: string
}, type?: string
{ goodsId?: string | number
color: "#04BE02", link?: string
title: "朋友圈", }>()
icon: "weixin-circle-fill",
type: 1, 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) 拼团商品分享以及店铺分享 {
color: "#04BE02",
props: ["thumbnail", "goodsName", "type", "goodsId", "link"], title: "朋友圈",
methods: { icon: "weixin-circle-fill",
close() { type: 1,
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
}, },
}; ])
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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "./mp-share.scss"; @import "./mp-share.scss";

View File

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

View File

@@ -10,198 +10,175 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
export default { import { ref, watch, onMounted, getCurrentInstance } from 'vue'
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;
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,
})
const emit = defineEmits(['update:modelValue', 'tapPopup'])
let y = this.dynamic const instance = getCurrentInstance()
? this.dynamicGetY(this.y, this.gap)
: this.transformRpx(this.y);
let x = this.dynamic const popupsTop = ref("0rpx")
? this.dynamicGetX(this.x, this.gap) const popupsLeft = ref("0rpx")
: this.transformRpx(this.x); const show = ref(false)
const dynPlace = ref("")
// #ifdef H5 onMounted(() => {
y = this.dynamic popupsPosition()
? this.y + statusBar })
: this.transformRpx(this.y + statusBar);
// #endif
this.dynPlace = const tapMask = () => {
this.placement == "default" emit("update:modelValue", !props.modelValue)
? this.getPlacement(x, y) }
: this.placement;
switch (this.dynPlace) { const tapItem = (item: any) => {
case "top-start": if (item.disabled) return
this.popupsTop = `${y + 9}rpx`; emit("tapPopup", item)
this.popupsLeft = `${x - 15}rpx`; emit("update:modelValue", !props.modelValue)
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;
return y; const getStatusBar = () => {
}, const windowInfo = uni.getWindowInfo()
dynamicGetX(x, gap) { // #ifdef H5
const { windowWidth: width } = uni.getWindowInfo(); return Promise.resolve(
x = x < gap ? gap : x; (windowInfo.statusBarHeight || 0) + (windowInfo.windowTop || 0)
x = width - x < gap ? width - gap : x; )
return x; // #endif
}, // #ifndef H5
transformRpx(params) { return Promise.resolve(windowInfo.statusBarHeight || 0)
const { screenWidth } = uni.getWindowInfo(); // #endif
return (params * screenWidth) / 375; }
},
}, const popupsPosition = async () => {
watch: { let statusBar = await getStatusBar()
value: { await new Promise<void>((resolve) => {
immediate: true, let popupsDom = uni.createSelectorQuery().in(instance!.proxy).select(".popups")
handler: async function (newVal, oldVal) { popupsDom
if (newVal) await this.popupsPosition(); .fields(
this.show = newVal; {
}, size: true,
}, },
placement: { (data: any) => {
immediate: true, if (!data) {
handler(newVal, oldVal) { resolve()
this.dynPlace = newVal; 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

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

View File

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

View File

@@ -10,211 +10,183 @@
</view> </view>
</view> </view>
</template> </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 { <script setup lang="ts">
name: "UniNumberBox", import { ref, watch } from 'vue'
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
}
}
if (type === "plus") { const props = withDefaults(defineProps<{
value += step; value?: number | string
if (value > (this.max * scale)) { modelValue?: number | string
return; min?: number
} max?: number
if (value < (this.min * scale)) { step?: number
value = this.min * scale 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); const emit = defineEmits<{
this.$emit("change", +this.inputValue); change: [value: number]
// TODO vue2 兼容 input: [value: number]
this.$emit("input", +this.inputValue); 'update:modelValue': [value: number]
// TODO vue3 兼容 blur: [event: Event]
this.$emit("update:modelValue", +this.inputValue); focus: [event: Event]
}, }>()
_getDecimalScale() {
let scale = 1; const inputValue = ref(0)
// 浮点型
if (~~this.step !== this.step) { watch(() => props.value, (val) => {
scale = Math.pow(10, String(this.step).split(".")[1].length); inputValue.value = +val
} })
return scale;
}, watch(() => props.modelValue, (val) => {
_onBlur(event) { inputValue.value = +val
this.$emit('blur', event) })
let value = event.detail.value;
if (!value) { if (props.value === 1) {
// this.inputValue = 0; inputValue.value = +props.modelValue
return; }
} if (props.modelValue === 1) {
value = +value; inputValue.value = +props.value
if (value > this.max) { }
value = this.max;
} else if (value < this.min) { function _getDecimalScale() {
value = this.min; let scale = 1
} if (~~props.step !== props.step) {
const scale = this._getDecimalScale(); scale = Math.pow(10, String(props.step).split('.')[1].length)
this.inputValue = value.toFixed(String(scale).length - 1); }
this.$emit("change", +this.inputValue); return scale
this.$emit("input", +this.inputValue); }
},
_onFocus(event) { function _emitValue() {
this.$emit('focus', event) 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$box-height: 48rpx; $box-height: 48rpx;
$bg: #f5f5f5; $bg: #f5f5f5;
$br: 4rpx; $br: 4rpx;
$color: #333; $color: #333;
.uni-numbox { .uni-numbox {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
} }
.uni-numbox-btns { .uni-numbox-btns {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
display: flex; display: flex;
/* #endif */ /* #endif */
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0 8px; padding: 0 8px;
background-color: $bg; background-color: $bg;
/* #ifdef H5 */ /* #ifdef H5 */
cursor: pointer; cursor: pointer;
/* #endif */ /* #endif */
} }
.uni-numbox__value { .uni-numbox__value {
margin: 0 4rpx; margin: 0 4rpx;
background-color: $bg; background-color: $bg;
width: 70rpx; width: 70rpx;
height: $box-height; height: $box-height;
text-align: center; text-align: center;
font-size: 24rpx; font-size: 24rpx;
border-left-width: 0; border-left-width: 0;
border-right-width: 0; border-right-width: 0;
color: $color; color: $color;
} }
.uni-numbox__minus { .uni-numbox__minus {
border-top-left-radius: $br; border-top-left-radius: $br;
border-bottom-left-radius: $br; border-bottom-left-radius: $br;
} }
.uni-numbox__plus { .uni-numbox__plus {
border-top-right-radius: $br; border-top-right-radius: $br;
border-bottom-right-radius: $br; border-bottom-right-radius: $br;
} }
.uni-numbox--text { .uni-numbox--text {
// fix nvue line-height: 40rpx;
line-height: 40rpx; font-size: 40rpx;
font-weight: 300;
color: $color;
}
font-size: 40rpx; .uni-numbox .uni-numbox--disabled {
font-weight: 300; color: #c0c0c0 !important;
color: $color; /* #ifdef H5 */
} cursor: not-allowed;
/* #endif */
.uni-numbox .uni-numbox--disabled { }
color: #c0c0c0 !important;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
</style> </style>

View File

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

View File

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

View File

@@ -10,7 +10,7 @@
:password="isPassword" :password="isPassword"
:type="inputType" :type="inputType"
:maxlength="size" :maxlength="size"
@input="input" @input="handleInput"
@focus="inputFocus" @focus="inputFocus"
@blur="inputBlur" @blur="inputBlur"
/> />
@@ -37,7 +37,7 @@
</view> </view>
</view> </view>
</template> </template>
<script> <script setup lang="ts">
/** /**
* @description 输入验证码组件 * @description 输入验证码组件
* @property {string} type = [box|middle|bottom] - 显示类型 默认box -eg:bottom * @property {string} type = [box|middle|bottom] - 显示类型 默认box -eg:bottom
@@ -50,175 +50,155 @@
* @property {string} boxActiveColor - 光标聚焦到的框的颜色 默认:#000000 * @property {string} boxActiveColor - 光标聚焦到的框的颜色 默认:#000000
* @event {Function(data)} confirm - 输入完成 * @event {Function(data)} confirm - 输入完成
*/ */
export default { import { ref, watch, onMounted, nextTick, getCurrentInstance } from 'vue'
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);
});
});
});
},
// 输入框输入变化的回调 const emit = defineEmits(['update:modelValue', 'input', 'confirm'])
input(e) { const props = withDefaults(defineProps<{
const value = e.detail.value; modelValue?: string
this.code = value; value?: string
this.cursorVisible = value.length !== this.size; type?: string
this.$emit('update:modelValue', value); inputType?: string
this.$emit('input', value); size?: number
this.inputSuccess(value); 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'
})
// 输入完成回调 const instance = getCurrentInstance()
inputSuccess(value) { const codeInput = ref<any>(null)
if (value.length === this.size) { const focused = ref(false)
this.$emit('confirm', value); const cursorVisible = ref(false)
} const cursorHeight = ref(35)
}, const code = ref('') // 输入的验证码
// 输入聚焦 const codeCursorLeft = ref<number[]>([]) // 向左移动的距离数组
inputFocus() {
this.cursorVisible = this.code.length !== this.size; // 初始化
}, watch([() => props.modelValue, () => props.value], ([modelVal, val]) => {
// 输入失去焦点 code.value = modelVal || val || ''
inputBlur() { }, { immediate: true })
this.cursorVisible = false;
}, onMounted(() => {
codeFormat(val, isPassword) { focused.value = props.isFocus
let value = ''; cursorVisible.value = props.isFocus
if (val) { init()
value = isPassword ? '*' : val; if (props.isFocus) {
} nextTick(() => {
return value; focusInput()
} })
},
watch: {
modelValue(val) {
this.code = val || '';
},
value(val) {
this.code = val || '';
}
} }
}; })
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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.xt__verify-code { .xt__verify-code {

View File

@@ -8,9 +8,7 @@ const dev = {
common: "https://common-api.pickmall.cn", common: "https://common-api.pickmall.cn",
buyer: "https://buyer-api.pickmall.cn", buyer: "https://buyer-api.pickmall.cn",
mqtt: "wss://lilishop-mqtt-pull.dllll.xyz/mqtt", mqtt: "wss://lilishop-mqtt-pull.dllll.xyz/mqtt",
common: "http://192.168.31.244:8890",
buyer: "http://192.168.31.244:8888",
im: "http://192.168.0.113:8885",
}; };
// 生产环境 // 生产环境
const prod = { const prod = {

View File

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

View File

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

View File

@@ -1,302 +1,262 @@
<template> <template>
<view class="coupon-center"> <view class="coupon-center">
<div class="swiper-box"> <view class="coupon-list">
<div class="swiper-item"> <u-empty mode="coupon" style='margin-top: 20%;' text="没有优惠券了" v-if="whetherEmpty"></u-empty>
<div class="scroll-v" enableBackToTop="true" scroll-y> <view v-else class="coupon-card" v-for="(item, index) in couponList" :key="index">
<u-empty mode="coupon" style='margin-top: 20%;' text="没有优惠券了" v-if="whetherEmpty"></u-empty> <view class="coupon-card-left">
<view v-else class="coupon-item" v-for="(item, index) in couponList" :key="index"> <text class="coupon-price-symbol">¥</text>
<view class="left"> <text class="coupon-price-value">{{ item.couponType == 'DISCOUNT' ? item.couponDiscount + '折' : unitPrice(item.price) }}</text>
<view class="wave-line"> </view>
<view class="wave" v-for="(item, index) in 12" :key="index"></view> <view class="coupon-divider"></view>
</view> <view class="coupon-card-right">
<view class="message"> <view class="coupon-info">
<view> <text class="coupon-card-name">{{ item.storeName == 'platform' ? '全平台' : item.storeName + '店铺' }}使用</text>
<!--判断当前优惠券类型 couponType PRICE || DISCOUNT --> <text class="coupon-card-desc">{{unitPrice(item.consumeThreshold) }}元可用</text>
<span v-if="item.couponType == 'DISCOUNT'">{{ item.couponDiscount }}</span> <text class="coupon-card-time" v-if="item.endTime">有效期至:{{ item.endTime.split(" ")[0] }}</text>
<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> </view>
<view class="coupon-claim-btn" @click="receive(item)">
</div> 领取
</div> </view>
</div> </view>
</view>
</view>
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { import { receiveCoupons } from '@/api/members.js'
receiveCoupons import { getAllCoupons } from '@/api/promotions.js'
} from "@/api/members.js"; import { useStore } from '@/store'
import { import { unitPrice } from '@/utils/filters.js'
getAllCoupons import {
} from "@/api/promotions.js"; onLoad,
export default { onNavigationBarButtonTap,
data() { onPullDownRefresh,
return { onReachBottom,
loadStatus: "more", //下拉状态 } from '@dcloudio/uni-app'
whetherEmpty: false, //是否为空 import { getCurrentInstance, ref } from 'vue'
couponList: [], // 优惠券列表
params: {
pageNumber: 1,
pageSize: 10,
},
storeId: "", //店铺 id,
couponData: ""
};
},
onLoad(option) {
this.storeId = option.storeId;
this.getCoupon();
},
onReachBottom() {
this.loadMore() const store = useStore()
}, const { proxy } = getCurrentInstance()!
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 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)
fetchCoupon(val){ onLoad((option) => {
receiveCoupons(val.id).then((res) => { storeId.value = option.storeId || ''
if (res.data.code == 200) { getCoupon()
uni.showToast({ })
title: "领取成功",
icon: "none",
});
} else {
uni.showToast({
title: res.data.message,
icon: "none",
});
}
});
},
/** onReachBottom(() => {
* 加载更多 loadMore()
*/ })
loadMore() {
if (this.couponData.total > this.params.pageNumber * this.params.pageSize) { onPullDownRefresh(() => {
this.params.pageNumber++; params.value.pageNumber = 1
this.getCoupon(); couponList.value = []
} getCoupon()
}, })
},
onNavigationBarButtonTap(e) { onNavigationBarButtonTap(() => {
uni.navigateTo({ uni.navigateTo({
url: "/pages/cart/coupon/couponIntro", 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> </script>
<style>
page {
height: 100%;
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
.coupon-center { .coupon-center {
height: 100%; min-height: 100vh;
background: #f7f8fa;
}
.swiper-box { .coupon-list {
.coupon-item { padding: 24rpx;
display: flex; }
align-items: center;
height: 220rpx;
margin: 20rpx;
.left { .coupon-card {
height: 100%; display: flex;
width: 260rpx; align-items: stretch;
background-color: $light-color; background: #fff;
position: relative; 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 { &:active {
color: $font-color-white; transform: scale(0.98);
display: flex; }
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 40rpx;
view:nth-child(1) { &::before,
font-weight: bold; &::after {
font-size: 60rpx; 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) { &::before {
font-size: $font-sm; top: -16rpx;
} }
}
.wave-line { &::after {
height: 220rpx; bottom: -16rpx;
width: 8rpx; }
position: absolute; }
top: 0;
left: 0;
background-color: $light-color;
overflow: hidden;
.wave { .coupon-card-left {
width: 8rpx; width: 220rpx;
height: 16rpx; min-height: 180rpx;
background-color: #ffffff; display: flex;
border-radius: 0 16rpx 16rpx 0; align-items: center;
margin-top: 4rpx; justify-content: center;
} flex-shrink: 0;
} color: #ff3b30;
position: relative;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
}
.circle { .coupon-price-symbol {
width: 40rpx; font-size: 32rpx;
height: 40rpx; font-weight: 700;
background-color: $bg-color; margin-right: 4rpx;
position: absolute; }
border-radius: 50%;
z-index: 111;
}
.circle-top { .coupon-price-value {
top: -20rpx; font-size: 56rpx;
right: -20rpx; font-weight: 900;
} line-height: 1;
letter-spacing: -1rpx;
}
.circle-bottom { .coupon-divider {
bottom: -20rpx; position: absolute;
right: -20rpx; left: 220rpx;
} top: 24rpx;
} bottom: 24rpx;
width: 0;
border-left: 2rpx dashed #eceef2;
}
.right { .coupon-card-right {
display: flex; flex: 1;
justify-content: space-between; padding: 32rpx 32rpx 32rpx 40rpx;
align-items: center; display: flex;
width: 450rpx; align-items: center;
font-size: $font-sm; justify-content: space-between;
height: 100%; gap: 20rpx;
background-color: #ffffff; background: #fff;
overflow: hidden; }
position: relative;
>view:nth-child(1) { .coupon-info {
color: #666666; display: flex;
margin-left: 20rpx; flex-direction: column;
display: flex; gap: 12rpx;
height: 100%; flex: 1;
flex-direction: column; min-width: 0;
justify-content: space-around; }
>view:nth-child(1) { .coupon-card-name {
color: #ff6262; font-size: 30rpx;
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 { .coupon-card-desc {
color: #ffffff; font-size: 24rpx;
background-color: $main-color; color: #8a8f99;
border-radius: 50%; font-weight: 500;
width: 86rpx; }
height: 86rpx;
text-align: center;
margin-right: 30rpx;
vertical-align: middle;
padding-top: 8rpx;
position: relative;
z-index: 2;
}
.bg-quan { .coupon-card-time {
width: 244rpx; font-size: 22rpx;
height: 244rpx; color: #b0b3bf;
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-title { .coupon-claim-btn {
width: 260rpx; 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> </style>

View File

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

View File

@@ -59,81 +59,59 @@
</view> </view>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { useCoupon } from "@/api/trade.js"; 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'
export default { const store = useStore()
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);
},
},
mounted() { const lightColor = computed(() => store.getters.lightColor)
this.init(); const current = ref(0)
console.log(this.routerVal); const list = [
}, { name: '可用优惠券' },
{ name: '不可用优惠券' },
]
const couponsList = ref<any[]>([])
const routerVal = ref<Record<string, any>>({})
methods: { onLoad((options) => {
/** routerVal.value = options || {}
* 从vuex中拿取优惠券信息 })
*/
init() { watch(current, (val) => {
this.couponsList = this.$store.state.canUseCoupons; couponsList.value = val == 0
}, ? store.state.canUseCoupons
/** : store.state.cantUseCoupons
* 领取优惠券 })
*/
clickWay(coupon) { onMounted(() => {
useCoupon({ init()
memberCouponId: coupon.id, })
used: !this.routerVal.selectedCoupon.includes(coupon.id),
way: this.routerVal.way, function init() {
}).then((res) => { couponsList.value = store.state.canUseCoupons
if (res.data.success) { }
uni.navigateBack();
} else { function clickWay(coupon: any) {
uni.showToast({ useCoupon({
title: res.data.message, memberCouponId: coupon.id,
duration: 2000, used: !routerVal.value.selectedCoupon.includes(coupon.id),
icon: "none", way: routerVal.value.way,
}); }).then((res) => {
} if (res.data.success) {
}); uni.navigateBack()
}, } else {
}, uni.showToast({
}; title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.desc { .desc {

View File

@@ -1,16 +1,15 @@
<template> <template>
<view class="b-content"> <view class="b-content">
<view class="navbar"> <view class="coupon-tabs">
<!-- 循环出头部tab栏 --> <u-tabs
<view :list="navList"
v-for="(item, index) in navList" keyName="text"
:key="index" :scrollable="false"
class="nav-item" :inactiveStyle="{ color: '#333' }"
@click="handleTabClick(index)" v-model:current="tabCurrentIndex"
><text :class="{ current: tabCurrentIndex === index }">{{ :lineColor="lightColor"
item.text :activeStyle="{ color: lightColor }"
}}</text></view ></u-tabs>
>
</view> </view>
<swiper <swiper
:current="tabCurrentIndex" :current="tabCurrentIndex"
@@ -28,90 +27,48 @@
scroll-y scroll-y
@scrolltolower="loadData" @scrolltolower="loadData"
> >
<!-- 空白页 -->
<u-empty <u-empty
mode="coupon" mode="coupon"
text="暂无优惠券了" text="暂无优惠券了"
v-if="navItem.whetherEmpty" v-if="navItem.whetherEmpty"
></u-empty> ></u-empty>
<!-- 数据 -->
<view <view
class="coupon-item" class="coupon-card"
:class="{ 'coupon-used': navIndex != 0 }" :class="{ 'coupon-used': navIndex != 0, 'coupon-expired': navIndex == 2 }"
v-for="(coupon, index) in navItem.dataList" v-for="(coupon, index) in navItem.dataList"
:key="index" :key="index"
> >
<view class="left"> <view class="coupon-card-left">
<view class="wave-line"> <text class="coupon-price-symbol" v-if="coupon.couponType != 'DISCOUNT'">¥</text>
<view <text class="coupon-price-value">{{ coupon.couponType == 'DISCOUNT' ? coupon.discount + '折' : unitPrice(coupon.price) }}</text>
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>
<view class="right" v-if="coupon"> <view class="coupon-divider"></view>
<view class="content"> <view class="coupon-card-right">
<view class="title-1">{{ coupon.title }}</view> <view class="coupon-info">
<view class="title-2" <text class="coupon-card-name">{{ coupon.title }}</text>
>使用范围{{ <text class="coupon-card-desc">使用范围{{
coupon.scopeType == "ALL" && coupon.storeId == "0" coupon.scopeType == "ALL" && coupon.storeId == "0"
? "全平台" ? "全平台"
: coupon.scopeType == "PORTION_GOODS" : coupon.scopeType == "PORTION_GOODS"
? "部分商品" ? "部分商品"
: coupon.scopeType == "PORTION_GOODS_CATEGORY" : coupon.scopeType == "PORTION_GOODS_CATEGORY"
? "部分分类商品" ? "部分分类商品"
: coupon.storeName == "platform" : coupon.storeName == "platform"
? "全平台" ? "全平台"
: coupon.storeName + "" : coupon.storeName + ""
}}使用</view }}使用</text>
> <text class="coupon-card-time" v-if="coupon.endTime">{{ coupon.endTime }}</text>
<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>
<view class="jiao-1" v-if="navIndex == 0"> <view class="coupon-status-btn" v-if="navIndex == 0" @click="useItNow(coupon)">
<text class="text-1">新到</text> 立即使用
<text class="text-2" v-if="coupon.used_status == 1"
>将过期</text
>
</view> </view>
<image <view class="coupon-status-btn disabled" v-else-if="navIndex == 1">
class="no-icon" 已使用
v-if="navIndex == 1" </view>
src="@/static/img/used.png" <view class="coupon-status-btn disabled" v-else>
></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>
<view class="bg-quan"> </view>
</view> </view>
</view> </view>
<uni-load-more :status="navItem.loadStatus"></uni-load-more> <uni-load-more :status="navItem.loadStatus"></uni-load-more>
@@ -121,351 +78,316 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getMemberCoupons } from "@/api/members.js"; 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'
export default { const store = useStore()
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() { const lightColor = computed(() => store.getters.lightColor)
this.navList[this.tabCurrentIndex].params.pageNumber = 1; const tabCurrentIndex = ref(0)
this.navList[this.tabCurrentIndex].dataList = []; const navList = ref([
this.getData(); {
}, text: '未使用',
loadStatus: 'more',
watch: { dataList: [] as any[],
/** params: {
* 监听切换顶部tab栏实现刷新数据 memberCouponStatus: 'NEW',
*/ pageNumber: 1,
tabCurrentIndex(val) { pageSize: 10,
if (this.navList[val].dataList.length == 0) this.getData(); status: 1,
}, },
whetherEmpty: false,
}, },
methods: { {
/** text: '已使用',
* 顶部tab点击 loadStatus: 'more',
*/ dataList: [] as any[],
handleTabClick(index) { params: {
this.tabCurrentIndex = index; 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() { getData()
uni.showLoading({ })
title: "加载中",
}); watch(tabCurrentIndex, (val) => {
let index = this.tabCurrentIndex; if (navList.value[val].dataList.length == 0) getData()
getMemberCoupons(this.navList[index].params).then((res) => { })
uni.stopPullDownRefresh();
if (res.data.success) { function hideLoadingIfNeeded() {
let data = res.data.result.records; if (store.state.isShowToast) uni.hideLoading()
if (data.length == 0) { }
if (res.data.pageNumber == 1) {
this.navList[index].whetherEmpty = true; function handleTabClick(index: number) {
} else { tabCurrentIndex.value = index
this.navList[index].loadStatus = "noMore"; }
}
} else if (data.length < 10) { function getData() {
this.navList[index].loadStatus = "noMore"; uni.showLoading({ title: '加载中' })
this.navList[index].dataList.push(...data); const index = tabCurrentIndex.value
} else { getMemberCoupons(navList.value[index].params).then((res) => {
this.navList[index].dataList.push(...data); 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'
} }
if (this.$store.state.isShowToast){ uni.hideLoading() }; } else if (data.length < 10) {
}); navList.value[index].loadStatus = 'noMore'
}, navList.value[index].dataList.push(...data)
} else {
/** navList.value[index].dataList.push(...data)
* 切换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();
} }
}, }
hideLoadingIfNeeded()
})
}
/** function changeTab(e: any) {
* 立即使用优惠券 tabCurrentIndex.value = e.detail.current
*/ }
useItNow(item) {
uni.navigateTo({
url: `/pages/navigation/search/searchPage?promotionsId=${item.couponId}&promotionType=COUPON`,
});
},
/** function loadData() {
* 优惠券详情 const index = tabCurrentIndex.value
*/ if (navList.value[index].loadStatus != 'noMore') {
couponDetail(item) { navList.value[index].params.pageNumber++
uni.navigateTo({ getData()
url: }
"/pages/cart/coupon/couponDetail?item=" + }
encodeURIComponent(JSON.stringify(item)),
}); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
page {
height: 100%;
}
$item-color: #fff;
.b-content { .b-content {
background: $page-color-base; background: #f7f8fa;
height: 100%; 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 { .swiper-box {
height: calc(100vh - 40px); height: calc(100vh - 88rpx - var(--status-bar-height));
} }
.list-scroll-content { .list-scroll-content {
height: 100%; height: 100%;
width: 100%; width: 100%;
padding: 24rpx;
box-sizing: border-box;
}
.coupon-item { .coupon-card {
display: flex; display: flex;
align-items: center; align-items: stretch;
height: 220rpx; background: #fff;
margin: 20rpx; 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;
.left { &:active {
height: 100%; transform: scale(0.98);
width: 260rpx; }
background-color: $light-color;
position: relative;
.message { &::before,
color: $font-color-white; &::after {
display: flex; content: "";
justify-content: center; position: absolute;
align-items: center; width: 32rpx;
flex-direction: column; height: 32rpx;
margin-top: 40rpx; background: #f7f8fa;
border-radius: 50%;
left: 204rpx;
z-index: 2;
box-shadow: inset 0 0 0 1rpx #eceef2;
}
view:nth-child(1) { &::before {
font-weight: bold; top: -16rpx;
font-size: 60rpx; }
}
view:nth-child(2) { &::after {
font-size: $font-sm; bottom: -16rpx;
} }
}
.wave-line { &.coupon-used {
height: 220rpx; .coupon-card-left {
width: 8rpx; background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
position: absolute; color: #999;
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;
}
} }
.coupon-status-btn {
background: #ccc;
box-shadow: none;
}
}
.right { &.coupon-expired {
display: flex; .coupon-card-left {
justify-content: space-between; background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
align-items: center; color: #999;
width: 450rpx; }
font-size: $font-sm; .coupon-status-btn {
height: 100%; background: #ccc;
background-color: #ffffff; box-shadow: none;
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 { .coupon-card-left {
width: 220rpx;
min-height: 180rpx;
display: flex; display: flex;
height: 80rpx; align-items: center;
padding: 0 5px; justify-content: center;
background: #fff; flex-shrink: 0;
color: $light-color; color: #ff3b30;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.06);
position: relative; position: relative;
z-index: 10; background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
}
.nav-item { .coupon-price-symbol {
flex: 1; font-size: 32rpx;
height: 100%; font-weight: 700;
font-size: 26rpx; margin-right: 4rpx;
color: $light-color; }
position: relative;
text-align: center; .coupon-price-value {
text { font-size: 56rpx;
line-height: 80rpx; font-weight: 900;
} line-height: 1;
.current { letter-spacing: -1rpx;
font-weight: bold; }
font-size: 28rpx;
&:after { .coupon-divider {
content: ""; position: absolute;
position: absolute; left: 220rpx;
bottom: 10rpx; top: 24rpx;
left: 108rpx; bottom: 24rpx;
width: 30rpx; width: 0;
border-bottom: 2px solid $light-color; 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> </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>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import * as API_Trade from "@/api/trade"; import { ref, getCurrentInstance, onMounted } from 'vue'
import {payCallback} from '@/api/members' import { onLoad, onBackPress } from '@dcloudio/uni-app'
export default { import { useStore } from '@/store'
data() { import * as API_Trade from '@/api/trade'
return { import { payCallback } from '@/api/members'
//路径传参 import { unitPrice } from '@/utils/filters.js'
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,
}; const store = useStore()
}, const { proxy } = getCurrentInstance()!
onLoad(val) {
this.routerVal = val;
//初始化参数 const routerVal = ref<Record<string, string>>({})
// #ifdef APP-PLUS const cashierParams = ref<Record<string, any>>({ price: 0 })
this.paymentType = "APP"; const payList = ref<string[]>([])
this.paymentClient = "APP"; const sn = ref('')
//#endif const orderType = ref('')
// #ifdef MP-WEIXIN const paymentType = ref('')
this.paymentType = "WECHAT_MP"; const paymentClient = ref('')
this.paymentClient = "MP"; const walletValue = ref(0)
//#endif const autoCancelTime = ref(0)
// #ifdef H5
this.paymentType = "H5";
//如果是微信浏览器则使用公众号支付否则使用h5
// 区别是h5是通过浏览器外部调用微信app进行支付而JSAPI则是 在微信浏览器内部,或者小程序 调用微信支付
this.paymentClient = this.isWeiXin() ? "JSAPI" : "H5";
//#endif
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 (routerVal.value.recharge_sn) {
uni.switchTab({ url: '/pages/tabbar/user/my' })
} else {
uni.navigateTo({ url: '/pages/order/myOrder?status=0' })
}
return true
}
return false
})
// onMounted(() => {
}, cashierData()
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: {
/** function hideLoadingIfNeeded() {
* 支付成功后跳转 if (store.state.isShowToast) uni.hideLoading()
*/ }
callback(paymentMethod){
uni.navigateTo({
url: "/pages/cart/payment/success?paymentMethod=" +
paymentMethod +
"&payPrice=" +
this.cashierParams.price+
"&orderType="+this.orderType
});
},
/** function callback(paymentMethod: string) {
* 获取收银详情 uni.navigateTo({
*/ url:
cashierData() { '/pages/cart/payment/success?paymentMethod=' +
let parms = {}; paymentMethod +
'&payPrice=' +
cashierParams.value.price +
'&orderType=' +
orderType.value,
})
}
if (this.routerVal.recharge_sn) { function cashierData() {
// 判断当前是否是充值 const parms: Record<string, string> = {}
this.sn = this.routerVal.recharge_sn;
this.orderType = "RECHARGE";
} else if (this.routerVal.trade_sn) { if (routerVal.value.recharge_sn) {
this.sn = this.routerVal.trade_sn; sn.value = routerVal.value.recharge_sn
this.orderType = "TRADE"; orderType.value = 'RECHARGE'
} else { } else if (routerVal.value.trade_sn) {
this.sn = this.routerVal.order_sn; sn.value = routerVal.value.trade_sn
this.orderType = "ORDER"; orderType.value = 'TRADE'
} } else {
parms.sn = this.sn; sn.value = routerVal.value.order_sn
parms.orderType = this.orderType; orderType.value = 'ORDER'
parms.clientType = this.paymentType; }
parms.sn = sn.value
parms.orderType = orderType.value
parms.clientType = paymentType.value
API_Trade.getCashierData(parms).then((res) => { API_Trade.getCashierData(parms).then((res) => {
if (res.data.success) {
cashierParams.value = res.data.result
if(res.data.success){ // #ifdef MP-WEIXIN
this.cashierParams = res.data.result; payList.value = res.data.result.support.filter((item: string) => item != 'ALIPAY')
// #endif
// #ifdef MP-WEIXIN if (routerVal.value.recharge_sn) {
this.payList = res.data.result.support.filter((item) => { payList.value = res.data.result.support.filter((item: string) => item != 'WALLET')
return item != "ALIPAY"; } else {
}); payList.value = res.data.result.support
// #endif }
// #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
if(this.routerVal.recharge_sn){ walletValue.value = res.data.result.walletValue
this.payList = res.data.result.support.filter((item) => { const cancelAt = Number(res.data.result.autoCancel)
return item != "WALLET"; autoCancelTime.value = cancelAt > 0 ? Math.max(cancelAt - Date.now(), 0) : 0
}) } else if (res.data.code == 32000) {
} setTimeout(() => {
else{ uni.redirectTo({ url: '/pages/order/myOrder?status=0' })
this.payList = res.data.result.support; }, 500)
} }
// #ifdef H5 })
//判断是否微信浏览器 }
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
this.payList = res.data.result.support.filter((item) => { function awaitPay(payment: string) {
return item != "ALIPAY"; proxy.$u.throttle(() => {
}); pay(payment)
// 充值的话仅保留微信支付 }, 2000)
if(this.orderType == "RECHARGE"){ }
this.payList = res.data.result.support.filter((item) => {
return item == "WECHAT";
});
}
} function padTime(val: number) {
// #endif return String(val ?? 0).padStart(2, '0')
}
function onPayTimeout() {
uni.showToast({ title: '支付超时,请重新下单', icon: 'none' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/order/myOrder?status=0' })
}, 1500)
}
function goPayError(message = '支付失败,如果您已支付,请勿反复支付') {
const query = [`orderSn=${encodeURIComponent(sn.value)}`, `message=${encodeURIComponent(message)}`]
uni.navigateTo({ url: `/pages/cart/payment/error?${query.join('&')}` })
}
async function pay(payment: string) {
const params = {
sn: sn.value,
orderType: orderType.value,
clientType: paymentType.value,
}
const paymentMethod = payment
const client = paymentClient.value
this.walletValue = res.data.result.walletValue; uni.showLoading({ title: '正在唤起支付...', mask: true })
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)
} // #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
awaitPay(payment){ // #ifdef MP-WEIXIN
this.$u.throttle(()=>{ await API_Trade.initiatePay(paymentMethod, client, params).then((res) => {
this.pay(payment) const response = res.data.result
}, 2000) 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
}
padTime(val) { function isWeiXin() {
return String(val ?? 0).padStart(2, '0'); const ua = window.navigator.userAgent.toLowerCase()
}, return ua.match(/MicroMessenger/i) == 'micromessenger'
}
onPayTimeout() {
uni.showToast({
title: '支付超时,请重新下单',
icon: 'none',
});
setTimeout(() => {
uni.redirectTo({
url: '/pages/order/myOrder?status=0',
});
}, 1500);
},
//订单支付
async pay(payment) {
// 支付编号
const sn = this.sn;
// 交易类型【交易号|订单号】
const orderType = this.orderType;
const clientType = this.paymentType;
let params = {
sn,
orderType,
clientType,
};
//支付方式 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
//#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
//#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;
}
},
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.method_icon { .method_icon {

View File

@@ -49,7 +49,13 @@
<!-- 倒计时 --> <!-- 倒计时 -->
<div class="count-down" v-if="!isOver && master.toBeGroupedNum"> <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>
<div class="user-list" v-if="data.pintuanMemberVOS"> <div class="user-list" v-if="data.pintuanMemberVOS">
@@ -59,173 +65,184 @@
</div> </div>
</div> </div>
<popupGoods :addr="addr" ref="popupGoods" :buyMask="maskFlag" @closeBuy="closePopupBuy" :goodsDetail="goodsDetail" :goodsSpec="goodsSpec" v-if="goodsDetail.id " @handleClickSku="getGoodsDetail" /> <popupGoods
<shares @close="closeShare" :link="'/pages/cart/payment/shareOrderGoods?sn='+this.routers.sn+'&sku='+this.routers.sku+'&goodsId='+this.routers.goodsId" type="pintuan" :addr="addr"
:thumbnail="data.promotionGoods.thumbnail" :goodsName="data.promotionGoods.goodsName" v-if="shareFlag " /> 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> </view>
</template> </template>
<script> <script setup lang="ts">
import { getGoods } from "@/api/goods.js"; import { ref, computed, watch, onMounted } from 'vue'
import { getPinTuanShare } from "@/api/order"; import { onLoad } from '@dcloudio/uni-app'
import shares from "@/components/m-share/index"; import { useStore } from '@/store'
import storage from "@/utils/storage.js"; import { getGoods } from '@/api/goods.js'
import popupGoods from "@/components/m-buy/goods"; //购物车商品的模块 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 { const store = useStore()
data() {
return { const popupGoodsRef = ref<any>(null)
flag: false, //判断接口是否正常请求 const flag = ref(false)
addr: { const addr = ref({ id: '' })
id: "", const maskFlag = ref(false)
}, const timeStamp = ref(0)
maskFlag: false, //商品弹框 const shareFlag = ref(false)
timeStamp: 0, const data = ref<any>(null)
shareFlag: false, const isMaster = ref(true)
data: "", const selectedGoods = ref<any>(null)
isMaster: true, const routers = ref<Record<string, string>>({})
selectedGoods: "", //选择的商品规格昵称 const goodsDetail = ref<any>({})
routers: "", //传参数据 const goodsSpec = ref<any>(null)
goodsDetail: "", //商品详情 const master = ref<any>(null)
goodsSpec: "", const PromotionList = ref<any>(null)
master: "", // 团长 const isGroup = ref(false)
PromotionList: "", //优惠集合 const isOver = ref(false)
isGroup: false, //是否拼团 const isBuy = ref(false)
isOver: false, //是否结束活动
isBuy: false, //当前用户是是否购买 const shareLink = computed(() => {
}; const { sn, sku, goodsId } = routers.value
}, return `/pages/cart/payment/shareOrderGoods?sn=${sn}&sku=${sku}&goodsId=${goodsId}`
components: { })
shares,
popupGoods, watch(isGroup, (val) => {
}, if (val) {
watch: { const timer = setInterval(() => {
isGroup(val) { if (popupGoodsRef.value) {
if (val) { popupGoodsRef.value.buyType = 'PINTUAN'
let timer = setInterval(() => {
this.$refs.popupGoods.buyType = "PINTUAN";
clearInterval(timer);
}, 100);
} else {
this.$refs.popupGoods.buyType = "";
} }
}, clearInterval(timer)
}, }, 100)
onLoad(options) { } else if (popupGoodsRef.value) {
this.routers = options; popupGoodsRef.value.buyType = ''
}, }
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;
// 获取当前时间时间戳 onLoad((options) => {
let dateTime = Date.parse(new Date()) / 1000; 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()
}
// 获取剩余拼团人数 function closeShare() {
this.master = shareFlag.value = false
res.data.result.pintuanMemberVOS.length != 0 && }
res.data.result.pintuanMemberVOS.filter((item) => {
return item.orderSn == "";
})[0];
// 获取当前是否是拼团本人 function onCountDownEnd() {
if ( isOver.value = true
storage.getUserInfo(this.routers.sku, this.routers.goodsId).id == }
this.master.memberId
) { function toBuy() {
this.isMaster = true; maskFlag.value = true
} else { if (!popupGoodsRef.value) return
this.isMaster = false; popupGoodsRef.value.parentOrder = {
// 获取商品详情 ...master.value,
this.getGoodsDetail({ orderSn: routers.value.sn,
id: this.routers.sku, }
goodsId: this.routers.goodsId, 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
} }
})
}
})
}
// 获取当前商品是否已经购买 function handleClickHome() {
if (storage.getUserInfo().id) { uni.switchTab({ url: '/pages/tabbar/home/index' })
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",
});
},
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -5,7 +5,7 @@
{{unitPrice(Number(payPrice)) }} {{unitPrice(Number(payPrice)) }}
</div> </div>
<div class="pay-btns"> <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 @click="navigateTo('/pages/tabbar/home/index', 'switch')">回到首页</div>
</div> </div>
</div> </div>
@@ -24,73 +24,55 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import goodsRecommend from "@/components/m-goods-recommend"; import { ref } from 'vue'
export default { import { onLoad } from '@dcloudio/uni-app'
data() { import goodsRecommend from '@/components/m-goods-recommend'
return { import { unitPrice } from '@/utils/filters.js'
checked: false,
paymentMethod: "",
from: "", const paymentMethod = ref('')
payPrice: 0, const from = ref('')
goodsList: [], const payPrice = ref<number | string>(0)
activeColor: this.$mainColor, const orderType = ref('')
};
},
components: {
goodsRecommend,
},
onLoad(options) {
this.paymentMethod = options.paymentMethod || "";
this.from = options.from || "";
this.payPrice = options.payPrice || 0;
this.orderType = options.orderType;
}, onLoad((options) => {
methods: { paymentMethod.value = options.paymentMethod || ''
paymentTypeFilter(val) { from.value = options.from || ''
switch (val) { payPrice.value = options.payPrice || 0
case "WECHAT": orderType.value = options.orderType || ''
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");
}
},
navigateTo(url, type) { function paymentTypeFilter(val: string) {
if (type === "switch") { switch (val) {
uni.switchTab({ case 'WECHAT':
url, return '微信'
}); case 'ALIPAY':
} else { return '支付宝'
uni.redirectTo({ case 'WALLET':
url, return '余额支付'
}); default:
} return ''
}, }
}, }
};
</script> function checkOrder() {
<style scoped lang="scss"> 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 { .subscribe {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;

View File

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

View File

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

View File

@@ -42,126 +42,95 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import * as API_Trade from "@/api/trade"; import { ref } from 'vue'
import * as API_Address from "@/api/address.js"; import { onLoad, onShow, onPullDownRefresh } from '@dcloudio/uni-app'
export default { import { useStore } from '@/store'
data() { import * as API_Trade from '@/api/trade'
return { import * as API_Address from '@/api/address.js'
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);
uni.navigateBack({ const store = useStore()
delta: 1,
});
},
//获取地址列表
getAddressList() {
uni.showLoading();
API_Address.getAddressList( const addressList = ref<any[]>([])
this.params.pageNumber, const showAction = ref(false)
this.params.pageSize const removeList = [{ text: '确定' }]
).then((res) => { const tips = { text: '确定要删除该收货人信息吗?' }
res.data.result.records.forEach((item) => { const removeId = ref('')
item.consigneeAddressPath = item.consigneeAddressPath.split(","); const routerVal = ref<Record<string, string>>({})
}); const params = { pageNumber: 1, pageSize: 1000 }
this.addressList = res.data.result.records;
console.log(this.addressList);
if (this.$store.state.isShowToast){ uni.hideLoading() }; onPullDownRefresh(() => {
}); addressList.value = []
}, getAddressList()
//删除地址 })
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;
item.isDefault ? "" : (item.isDefault = !item.isDefault); onLoad((val) => {
routerVal.value = val || {}
})
API_Address.editAddress(item).then((res) => { onShow(() => {
uni.showToast({ addressList.value = []
title: "设置默认地址成功", getAddressList()
icon: "none", })
});
this.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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

@@ -20,67 +20,57 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import * as API_Trade from "@/api/trade"; import { ref } from 'vue'
import * as API_Store from "@/api/store.js"; import { onLoad, onShow, onPullDownRefresh } from '@dcloudio/uni-app'
export default { import { useStore } from '@/store'
data() { import * as API_Trade from '@/api/trade'
return { import * as API_Store from '@/api/store.js'
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);
uni.navigateBack({ const store = useStore()
delta: 1,
});
},
//获取地址列表
getAddressList() {
uni.showLoading();
API_Store.getStoreAddress( const storeAddressList = ref<any[]>([])
this.routerVal.storeId, const showAction = ref(false)
this.params const removeList = [{ text: '确定' }]
).then((res) => { const tips = { text: '确定要删除该收货人信息吗?' }
this.storeAddressList = res.data.result.records; const removeId = ref('')
console.log(this.storeAddressList); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

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

View File

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

View File

@@ -19,40 +19,30 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { recharge } from "@/api/members"; import { ref, watch } from 'vue'
export default { import { recharge } from '@/api/members'
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;
}
const res = await recharge({ price: amount }); const price = ref('')
if (res.data.success) { const flag = ref(true)
uni.navigateTo({
url: `/pages/cart/payment/payOrder?orderType=RECHARGE&recharge_sn=${res.data.result.rechargeSn}`, 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -25,182 +25,149 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getWithdrawApplyPage, getWithdrawApplyWechatTransferInfo } from "@/api/members"; import { ref } from 'vue'
export default { import { onShow } from '@dcloudio/uni-app'
data() { import { getWithdrawApplyPage, getWithdrawApplyWechatTransferInfo } from '@/api/members'
return { import { unitPrice } from '@/utils/filters.js'
loaded: false,
params: { const loaded = ref(false)
pageNumber: 1, const params = ref({
pageSize: 10, pageNumber: 1,
order: "desc", pageSize: 10,
}, order: 'desc',
records: [], })
}; const records = ref<any[]>([])
},
onShow() { onShow(() => {
this.params.pageNumber = 1; params.value.pageNumber = 1
this.records = []; records.value = []
this.loaded = false; loaded.value = false
this.getData(); getData()
}, })
methods: {
withdrawStatusText(applyStatus) { function withdrawStatusText(applyStatus: string) {
switch (applyStatus) { switch (applyStatus) {
case "APPLY": case 'APPLY':
return "申请中"; return '申请中'
case "VIA_AUDITING": case 'VIA_AUDITING':
return "审核通过"; return '审核通过'
case "D_VIA_AUDITING": case 'D_VIA_AUDITING':
return "分销提现审核通过"; return '分销提现审核通过'
case "FAIL_AUDITING": case 'FAIL_AUDITING':
return "审核未通过"; return '审核未通过'
case "D_FAIL_AUDITING": case 'D_FAIL_AUDITING':
return "分销提现审核未通过"; return '分销提现审核未通过'
case "WAIT_USER_CONFIRM": case 'WAIT_USER_CONFIRM':
return "等待用户确认"; return '等待用户确认'
case "SUCCESS": case 'SUCCESS':
return "提现成功"; return '提现成功'
case "ERROR": case 'ERROR':
return "提现失败"; return '提现失败'
default: default:
return applyStatus || ""; 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() { if (!mchId || !appId || !wechatPackage) {
getWithdrawApplyPage(this.params).then((res) => { uni.showToast({ title: '微信确认参数缺失', duration: 2000, icon: 'none' })
this.loaded = true; return
if (res.data.success) { }
if (res.data.result.records.length != 0) {
this.records.push(...res.data.result.records); 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({ uni.showToast({
title: "缺少提现记录ID", title: errMsg ? `唤起失败:${errMsg}` : '唤起失败',
duration: 2000, duration: 2500,
icon: "none", icon: 'none',
}); })
return;
} }
uni.showLoading({ if (typeof wx !== 'undefined' && wx.getSystemInfoSync) {
title: "加载中", try {
}); const sys = wx.getSystemInfoSync()
getWithdrawApplyWechatTransferInfo(id) if (sys && sys.platform === 'devtools') {
.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) {
uni.showToast({ uni.showToast({
title: "微信确认参数缺失", 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}` : "唤起失败",
duration: 2500, duration: 2500,
icon: "none", 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) {}
} }
} catch (e) {}
}
if (typeof wx !== "undefined" && wx.canIUse && wx.canIUse("requestMerchantTransfer")) { if (typeof wx !== 'undefined' && wx.canIUse && wx.canIUse('requestMerchantTransfer')) {
wx.requestMerchantTransfer({ wx.requestMerchantTransfer({
mchId, mchId,
appId, appId,
package: wechatPackage, package: wechatPackage,
success: (r) => { success: (r: any) => openResultToast(r && (r.errMsg || r.err_msg)),
openResultToast(r && (r.errMsg || r.err_msg)); fail: (r: any) => 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",
});
}) })
.finally(() => { return
uni.hideLoading(); }
});
}, 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -58,77 +58,68 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getUserWallet, withdrawalApply, withdrawalSettingVO } from "@/api/members"; import { ref, computed, getCurrentInstance, onMounted } from 'vue'
export default { import { getUserWallet, withdrawalApply, withdrawalSettingVO } from '@/api/members'
data() { import { unitPrice } from '@/utils/filters.js'
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;
}
const params = { price: amount }; const { proxy } = getCurrentInstance()!
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;
}
withdrawalApply(params).then((res) => { const price = ref('')
if (res.data.success) { const walletNum = ref(0)
uni.showToast({ const minPrice = ref(0)
title: "提现成功!", const type = ref('')
duration: 2000, const connectNumber = ref('')
icon: "none", const realName = ref('')
});
setTimeout(() => { const typeLabel = computed(() => {
uni.navigateBack({ delta: 1 }); if (type.value === 'ALI') return '支付宝'
}, 1000); if (type.value) return '微信'
} return '--'
}); })
},
handleAll() { onMounted(async () => {
this.price = String(this.walletNum || ""); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

@@ -3,160 +3,100 @@
<view> <view>
<h4>实名认证请上传真实的个人信息认证通过后将无法修改</h4> <h4>实名认证请上传真实的个人信息认证通过后将无法修改</h4>
<view> <view>
<u-form :model="ruleForm" label-width="200rpx" ref="uForm"> <up-form
<u-form-item label="姓名" prop="name"> :model="formData"
<u-input v-model="ruleForm.name" placeholder="请输入您的真实姓名" /> label-position="left"
</u-form-item> label-width="180rpx"
<u-form-item label="身份证" prop="idNumber"> ref="uFormRef"
<u-input v-model="ruleForm.idNumber" placeholder="请输入身份证号码" /> >
</u-form-item> <up-form-item label="姓名" prop="name">
<u-form-item label="银行开户行" prop="settlementBankBranchName"> <u-input v-model="formData.name" placeholder="请输入您的真实姓名" />
<u-input v-model="ruleForm.settlementBankBranchName" type="text" placeholder="请输入银行开户行" /> </up-form-item>
</u-form-item> <up-form-item label="身份证" prop="idNumber">
<u-form-item label="银行开户名" prop="settlementBankAccountName"> <u-input v-model="formData.idNumber" placeholder="请输入身份证号码" />
<u-input v-model="ruleForm.settlementBankAccountName" type="text" placeholder="请输入银行开户名" /> </up-form-item>
</u-form-item> <up-form-item label="银行开户行" prop="settlementBankBranchName">
<u-form-item label="银行账号" prop="settlementBankAccountNum"> <u-input v-model="formData.settlementBankBranchName" placeholder="请输入银行开户行" />
<u-input v-model="ruleForm.settlementBankAccountNum" type="text" placeholder="请输入银行账号" /> </up-form-item>
</u-form-item> <up-form-item label="银行开户名" prop="settlementBankAccountName">
<!-- <u-form-item label="身份证正面照" prop="name"> <u-input v-model="formData.settlementBankAccountName" placeholder="请输入银行开户名" />
<u-upload></u-upload> </up-form-item>
</u-form-item> <up-form-item label="银行账号" prop="settlementBankAccountNum">
<u-form-item label="身份证反面照" prop="name"> <u-input v-model="formData.settlementBankAccountNum" placeholder="请输入银行账号" />
<u-upload></u-upload> </up-form-item>
</u-form-item> </up-form>
<u-form-item label="手持身份证照" prop="name"> <u-button :customStyle="{ background: lightColor, color: '#fff' }" @click="submitForm">提交</u-button>
<u-upload></u-upload>
</u-form-item> -->
</u-form>
<u-button :customStyle="{'background':$lightColor,'color':'#fff' }" @click="submit">提交</u-button>
</view> </view>
</view> </view>
<view class="tips"> <view class="tips">
<view>您提交的信息正在审核</view> <view>您提交的信息正在审核</view>
<view>提交认证申请后工作人员将在三个工作日进行核对完成审核</view> <view>提交认证申请后工作人员将在三个工作日进行核对完成审核</view>
</view> </view>
</view> </view>
</template> </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(() => { <script setup lang="ts">
uni.navigateBack(); import { reactive, computed, ref, getCurrentInstance } from 'vue'
}, 500); import { onReady } from '@dcloudio/uni-app'
} else { import { useStore } from '@/store'
uni.showToast({ import { applyDistribution } from '@/api/goods'
title: res.data.message,
duration: 2000, const store = useStore()
icon: "none", const { proxy } = getCurrentInstance()!
});
} const lightColor = computed(() => store.getters.lightColor)
}); const uFormRef = ref<any>(null)
} else {
uni.showToast({ const formData = reactive({
title: "请填写有效信息", name: '',
duration: 2000, idNumber: '',
icon: "none", 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() { settlementBankBranchName: [{ required: true, message: '请输入银行开户行', trigger: 'blur' }],
this.$refs.uForm.setRules(this.rules); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.wrapper { .wrapper {
padding: 32rpx; padding: 32rpx;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,60 +1,40 @@
<template> <template>
<div class="wrapper"> <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> </div>
</template> </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: { <script setup lang="ts">
init(option) { import { ref } from 'vue'
getArticleDetailByType(this.way[option.type].type).then((res) => { import { onLoad } from '@dcloudio/uni-app'
if (res.data.success) { import { getArticleDetailByType } from '@/api/article'
this.res = res.data.result;
console.log(res) 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.wrapper { .wrapper {
padding: 16rpx; padding: 16rpx;

View File

@@ -196,515 +196,356 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
// rpx和px的比率 import { ref, nextTick, getCurrentInstance } from 'vue'
var l
// 可用窗口高度
var wh
// 顶部空盒子的高度
var mgUpHeight
import { import {
getTalkMessage, onLoad,
onHide,
onUnload,
onPullDownRefresh,
onPageScroll,
} from '@dcloudio/uni-app'
import {
getTalkMessage as fetchTalkMessageApi,
getTalkByUser, getTalkByUser,
jumpObtain jumpObtain,
} from "@/api/im.js"; } from '@/api/im.js'
import SocketService from "@/utils/socket_service.js"; import SocketService from '@/utils/socket_service.js'
import storage from "@/utils/storage.js"; import storage from '@/utils/storage.js'
import { import { beautifyTime, unitPrice } from '@/utils/filters.js'
beautifyTime
} from "@/utils/filters.js"
import config from '@/config/config.js' import config from '@/config/config.js'
import { textReplaceEmoji, emojistwo } from '@/utils/emojis.js'; import { textReplaceEmoji, emojistwo } from '@/utils/emojis.js'
export default {
// 页面卸载后清除imGoodId
onUnload () {
// #ifdef H5
uni.setStorageSync("imGoodId", '');
// #endif
if (this.socketOpen == true) { // rpx和px的比率
uni.closeSocket(); let l: number
} // 可用窗口高度
}, let wh: number
onLoad (options) { // 顶部空盒子的高度
// 没有goodsid则不显示 发送商品弹窗 let mgUpHeight: number
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() const { proxy } = getCurrentInstance()!
l = query.screenWidth / 750 const socketOpen = ref(false)
wh = query.windowHeight const showHideModel = ref<string | undefined>(undefined)
this.scrollHeight = (query.windowHeight - 44) + "px" const localImGoodsId = ref('')
this.user = storage.getUserInfo() const showHide = ref(true)
this.toUser = storage.getTalkToUser() 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)
if (options.talkId) { onLoad((options: Record<string, string | undefined> = {}) => {
this.params.talkId = options.talkId; showHideModel.value = options.goodsid
this.getTalkMessage() // #ifdef H5
} else { localImGoodsId.value = uni.getStorageSync('imGoodId')
this.getTalk(options.userId) // #endif
resolve.value = options
} if (resolve.value.goodsid) {
commodityDetails()
// 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;
},
} }
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> </script>

View File

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

View File

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

View File

@@ -1,99 +1,113 @@
<template> <template>
<view class="container " style="font-size: 13px;"> <view class="container" style="font-size: 13px">
<block v-for="(row, index) in messageList" :key="index"> <block v-for="(row, index) in messageList" :key="index">
<view class="msgItem"> <view class="msgItem">
<div class="msgMsg"> <div class="msgMsg">
<div class="bagbar">{{$u.timeFormat(row.send_time, 'yyyy-mm-dd')}}</div> <div class="bagbar">{{ formatSendTime(row.send_time) }}</div>
</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> <u-card
<template #body> @click="navigateToLogisticsDetail(row.sn, row.logi_id, row.ship_no)"
<view class="msg-body"> :title="pageTitle"
<image class="msgImg" :src="row.goods_img" mode=""></image> title-color="#666666"
<view class="msgView"> title-size="24"
<view>{{row.goodsName}}</view> sub-title-color="#666666"
<view class="msgNum">订单号:{{row.sn}}</view> sub-title-size="24"
</view> :border="false"
</view> :sub-title="row.status"
</template> >
</u-card> <template #body>
</view> <view class="msg-body">
</block> <image class="msgImg" :src="row.goods_img" mode=""></image>
<uni-load-more :status="loadStatus"></uni-load-more> <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> </view>
</template> </template>
<script> <script setup lang="ts">
import * as API_Message from "@/api/message.js"; import { ref, getCurrentInstance } from 'vue'
export default { import { onLoad, onReachBottom } from '@dcloudio/uni-app'
data() { import { useStore } from '@/store'
return { import * as API_Message from '@/api/message.js'
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)
}
})
} 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> </script>
<style scoped lang='scss'> <style scoped lang="scss">
.ddnumber { .msg-body {
color: $u-tips-color; display: flex;
font-size: 24rpx; background-color: rgba(102, 110, 232, 0.0470588235294118);
} .msgImg {
.msg-body{ width: 160rpx;
display: flex; height: 160rpx;
background-color: rgba(102, 110, 232, 0.0470588235294118); }
.msgView {
margin-left: 20rpx;
.msgImg{ .msgNum:last-child {
width: 160rpx; margin-top: 60rpx;
height: 160rpx; }
} }
.msgView{
margin-left: 20rpx;
.msgNum:last-child{
margin-top: 60rpx;
}
}
} }
.bagbar { .bagbar {
display: inline; display: inline;
@@ -103,13 +117,8 @@ export default {
padding: 10rpx 20rpx; padding: 10rpx 20rpx;
background: $u-info-disabled; background: $u-info-disabled;
} }
.storeImg {
width: 100%;
height: 100rpx;
margin-right: 20rpx;
}
.container { .container {
background: #F9F9F9; background: #f9f9f9;
min-height: 100vh; min-height: 100vh;
} }
.msgMsg { .msgMsg {
@@ -119,12 +128,8 @@ export default {
.msgItem { .msgItem {
padding: 1em 0; padding: 1em 0;
} }
view{ view {
font-size: 13px; font-size: 13px;
color: #666666; color: #666666;
}
u-card{
font-size: 13px;
color: #666666;
} }
</style> </style>

View File

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

View File

@@ -30,20 +30,11 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { mapMutations } from "vuex"; // 占位页,后续接入客服消息
export default {
data() {
return {};
},
methods: {
...mapMutations(["logout"]),
}
};
</script> </script>
<style scoped lang='scss'> <style scoped lang="scss">
.msgTime { .msgTime {
font-size: 13px; font-size: 13px;
} }
@@ -56,11 +47,6 @@ page {
vertical-align: middle; vertical-align: middle;
border-radius: 0.4em; border-radius: 0.4em;
} }
.qicon {
text-align: center;
display: block;
font-size: 20px;
}
.redBox { .redBox {
padding: 10rpx 12rpx; padding: 10rpx 12rpx;
display: inline-block; display: inline-block;
@@ -71,7 +57,6 @@ page {
height: 1em; height: 1em;
background: #ed6533; background: #ed6533;
border-radius: 50%; border-radius: 50%;
color: #fff; color: #fff;
} }
.tit_title, .tit_title,
@@ -92,25 +77,14 @@ page {
} }
.list-cell { .list-cell {
align-items: baseline; align-items: baseline;
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
line-height: 60rpx; line-height: 60rpx;
position: relative; position: relative;
background: #fff; background: #fff;
justify-content: center; justify-content: center;
&.log-out-btn {
margin-top: 40rpx;
.cell-tit {
color: $uni-color-primary;
text-align: center;
margin-right: 0;
}
}
&.cell-hover { &.cell-hover {
background: #fafafa; background: #fafafa;
} }
&.b-b:after {
left: 30rpx;
}
&.m-t { &.m-t {
margin-top: 16rpx; margin-top: 16rpx;
} }
@@ -119,22 +93,12 @@ page {
margin-top: 10rpx; margin-top: 10rpx;
height: 60rpx; height: 60rpx;
display: flex; display: flex;
justify-content: center; //这个是X轴居中 justify-content: center;
align-items: center; //这个是 Y轴居中 align-items: center;
align-self: baseline; align-self: baseline;
font-size: $font-lg; font-size: $font-lg;
color: $font-color-light; color: $font-color-light;
margin-left: 10rpx; 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"> <view class="container">
<block v-for="(row, index) in messageList" :key="index"> <block v-for="(row, index) in messageList" :key="index">
<view class="msgItem"> <view class="msgItem">
<div class="is_read"> <div class="is_read">
<!-- {{row.is_read}} --> <span v-if="row.is_read"></span>
<span v-if="row.is_read"></span> <span v-else class="red">·</span>
<span v-else class="red">·</span> </div>
<div class="msgMsg">{{ formatSendTime(row.send_time) }}</div>
</div> <u-card :title="pageTitle" :title-size="35" :border="false">
<div class="msgMsg">{{$u.timeFormat(row.send_time, 'yyyy-mm-dd')}}</div>
<u-card :title="title" :title-size="35" :border="false">
<template #body> <template #body>
<view class="u-body-item u-flex u-row-between u-p-b-0"> <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> </view>
</template> </template>
</u-card> </u-card>
@@ -22,74 +20,85 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { mapMutations } from "vuex"; import { ref, getCurrentInstance } from 'vue'
import * as API_Message from "@/api/message.js"; import { onLoad, onReachBottom } from '@dcloudio/uni-app'
export default { import { useStore } from '@/store'
data() { import * as API_Message from '@/api/message.js'
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"]),
/** 获取站内消息 */ const store = useStore()
GET_MessageList(reset) { const { proxy } = getCurrentInstance()!
if (reset) {
this.params.pageNumber = 1; const pageTitle = '系统消息'
this.messageList = []; const loadStatus = ref('more')
} const queryParams = ref({ pageNumber: 1, pageSize: 5 })
uni.showLoading({ const messageList = ref<any[]>([])
title: "加载中"
}); onLoad(() => {
API_Message.getMessages(this.params).then(async response => { fetchMessageList(true)
if (this.$store.state.isShowToast){ uni.hideLoading() }; })
const { data } = response;
if (!data || !data.length) { onReachBottom(() => {
this.messageList.push(...data.data); queryParams.value.pageNumber++
this.handleReadPageMessages(); fetchMessageList(false)
} })
});
}, function hideLoadingIfNeeded() {
/** 设置消息已读 **/ if (store.state.isShowToast) uni.hideLoading()
handleReadPageMessages() { }
const ids = this.messageList.map(item => item.id).join(",");
API_Message.messageMarkAsRead(ids).then(async () => {}); 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> </script>
<style scoped lang='scss'> <style scoped lang="scss">
.is_read{ .is_read {
position: absolute; position: absolute;
right: 25px; right: 25px;
top: 80rpx; top: 80rpx;
z-index: 999; z-index: 999;
} }
.container { .container {
background: #f9f9f9; background: #f9f9f9;
min-height: 100vh; min-height: 100vh;
} }
.red{ .red {
color: coral; color: coral;
font-size: 100rpx; font-size: 100rpx;
} }
.msgMsg { .msgMsg {
text-align: center; text-align: center;

View File

@@ -14,7 +14,7 @@
:activeStyle="{ color: lightColor }" :activeStyle="{ color: lightColor }"
class="collect-tabs" class="collect-tabs"
:list="navList" :list="navList"
:scrollable="true" :scrollable="false"
v-model:current="tabCurrentIndex" v-model:current="tabCurrentIndex"
></u-tabs> ></u-tabs>
</view> </view>
@@ -22,278 +22,238 @@
</u-navbar> </u-navbar>
<view class="collect-body"> <view class="collect-body">
<!-- 显示商品栏 --> <!-- 显示商品栏 -->
<view v-if="tabCurrentIndex == 0" class="tab-content"> <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>
<u-empty style="margin-top: 40rpx" text="暂无收藏商品数据" mode="favor" v-if="goodsEmpty"></u-empty> <template v-else>
<template v-else> <u-swipe-action
<u-swipe-action v-for="(item, index) in goodsList"
v-for="(item, index) in goodList" :key="item.skuId || item.goodsId || index"
:key="index" class="collect-swipe"
class="collect-swipe" >
<u-swipe-action-item
@open="openSwipeItem(item, 'goods')"
:show="item.selected"
:options="swipeOptions"
@click="removeGoodsCollection(item, index)"
:name="index"
> >
<u-swipe-action-item <view class="goods" @click="goGoodsDetail(item)">
@open="openLeftChange(item, 'goods')" <u-image width="131rpx" height="131rpx" :src="item.image" mode="aspectFit">
:show="item.selected" <template #loading><u-loading-icon></u-loading-icon></template>
:options="LeftOptions" </u-image>
@click="clickGoodsSwiperAction(item, index)" <view class="goods-intro">
:name="index" <view class="goods-name">{{ item.goodsName }}</view>
> <view class="goods-sn">{{ item.goods_sn }}</view>
<view class="goods" @click="goGoodsDetail(item)"> <view class="goods-price">{{ unitPrice(item.price) }}</view>
<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> </view>
</u-swipe-action-item> </view>
</u-swipe-action> </u-swipe-action-item>
</template> </u-swipe-action>
</scroll-view> </template>
</view> </view>
<!-- 显示收藏的店铺栏 --> <!-- 显示收藏的店铺栏 -->
<view v-else class="tab-content"> <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>
<u-empty style="margin-top: 40rpx" text="暂无收藏店铺数据" mode="favor" v-if="storeEmpty"></u-empty> <template v-else>
<template v-else> <u-swipe-action
<u-swipe-action v-for="(item, index) in storeList"
v-for="(item, index) in storeList" :key="item.id || index"
:key="index" class="collect-swipe"
class="collect-swipe" >
<u-swipe-action-item
@open="openSwipeItem(item, 'store')"
:show="item.selected"
:options="swipeOptions"
@click="removeStoreCollection(item)"
:name="index"
> >
<u-swipe-action-item <view class="store" @click="goStoreMainPage(item.id)">
@open="openLeftChange(item, 'store')" <view class="intro">
:show="item.selected" <view class="store-logo">
:options="LeftOptions" <u-image width="102rpx" height="102rpx" :src="item.storeLogo" :alt="item.storeName"
@click="clickStoreSwiperAction(item)" mode="aspectFit">
:name="index" <template #loading><u-loading-icon></u-loading-icon></template>
> </u-image>
<view class="store" @click="goStoreMainPage(item.id)"> </view>
<view class="intro"> <view class="store-name">
<view class="store-logo"> <view>{{ item.storeName }}</view>
<u-image width="102rpx" height="102rpx" :src="item.storeLogo" :alt="item.storeName" <u-tag size="mini" type="error" :color="mainColor" v-if="item.selfOperated"
mode="aspectFit"> text="自营" mode="plain" shape="circle" />
<template #loading><u-loading></u-loading></template> </view>
</u-image> <view class="store-collect">
</view> <view>进店逛逛</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>
</view> </view>
</u-swipe-action-item> </view>
</u-swipe-action> </u-swipe-action-item>
</template> </u-swipe-action>
</scroll-view> </template>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { import { ref, computed } from 'vue'
getGoodsCollection, import { onShow, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
getStoreCollection, import { useStore } from '@/store'
deleteGoodsCollection, import { unitPrice } from '@/utils/filters.js'
deleteStoreCollection, import {
} from "@/api/members.js"; getGoodsCollection,
export default { getStoreCollection,
data() { deleteGoodsCollection,
return { deleteStoreCollection,
lightColor:this.$lightColor, } from '@/api/members.js'
// 商品左滑侧边栏
LeftOptions: [{
text: "取消",
style: {
backgroundColor: this.$lightColor,
},
}, ],
tabCurrentIndex: 0, //tab的下标默认为0也就是说会默认请求商品
navList: [
//tab显示数据
{
name: "商品(0)",
params: { const store = useStore()
pageNumber: 1,
pageSize: 10,
},
},
{
name: "店铺(0)",
params: { const lightColor = computed(() => store.getters.lightColor)
pageNumber: 1, const mainColor = computed(() => store.getters.mainColor)
pageSize: 10,
},
},
],
goodsEmpty: false, //商品数据是否为空 const swipeOptions = computed(() => [
storeEmpty: false, //店铺数据是否为空 {
goodList: [], //商品集合 text: '取消',
storeList: [], //店铺集合 style: { backgroundColor: lightColor.value },
}; },
}, ])
onShow() {
this.fetchReloadOrNextPage('reload')
},
onReachBottom() {
this.fetchReloadOrNextPage('next')
},
methods: { const tabCurrentIndex = ref(0)
// 刷新或者下一页 const navList = ref([
fetchReloadOrNextPage(type) { { name: '商品(0)', params: { pageNumber: 1, pageSize: 10 } },
if(type == 'next'){ { name: '店铺(0)', params: { pageNumber: 1, pageSize: 10 } },
this.navList[this.tabCurrentIndex].params.pageNumber ++; ])
if (this.tabCurrentIndex == 0) { const goodsEmpty = ref(false)
this.getGoodList(); const storeEmpty = ref(false)
} else { const goodsList = ref<any[]>([])
this.getStoreList(); const storeList = ref<any[]>([])
}
onShow(() => {
reloadOrLoadMore('reload')
})
onReachBottom(() => {
reloadOrLoadMore('next')
})
onPullDownRefresh(() => {
if (tabCurrentIndex.value === 0) {
navList.value[0].params.pageNumber = 1
goodsList.value = []
fetchGoodsList()
} else {
navList.value[1].params.pageNumber = 1
storeList.value = []
fetchStoreList()
}
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function reloadOrLoadMore(type: 'reload' | 'next') {
if (type === 'next') {
navList.value[tabCurrentIndex.value].params.pageNumber++
if (tabCurrentIndex.value === 0) {
fetchGoodsList()
} else {
fetchStoreList()
}
return
}
navList.value[0].params.pageNumber = 1
navList.value[1].params.pageNumber = 1
goodsEmpty.value = false
storeEmpty.value = false
goodsList.value = []
storeList.value = []
fetchGoodsList()
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) {
goodsList.value = []
navList.value[0].params.pageNumber = 1
fetchGoodsList()
}
})
}
function removeStoreCollection(val: any) {
deleteStoreCollection(val.id).then((res) => {
if (res.statusCode === 200) {
storeList.value = []
navList.value[1].params.pageNumber = 1
fetchStoreList()
}
})
}
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() {
uni.showLoading({ title: '加载中' })
getGoodsCollection(navList.value[0].params, 'GOODS')
.then((res) => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
if (res.data.success) {
const data = res.data.result
navList.value[0].name = `商品(${data.total})`
goodsEmpty.value = data.total === 0
if (data.records?.length) {
const records = data.records.map((item: any) => ({ ...item, selected: false }))
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();
} }
}, })
}; .catch(() => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
})
}
function fetchStoreList() {
uni.showLoading({ title: '加载中' })
getStoreCollection(navList.value[1].params, 'STORE')
.then((res) => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
if (res.data.success) {
const data = res.data.result
navList.value[1].name = `店铺(${data.total})`
storeEmpty.value = data.total === 0
if (data.records?.length) {
const records = data.records.map((item: any) => ({ ...item, selected: false }))
storeList.value.push(...records)
}
}
})
.catch(() => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
})
}
</script> </script>
<style lang="scss"> <style lang="scss">
@@ -324,15 +284,18 @@
.collect-body { .collect-body {
flex: 1; flex: 1;
min-height: 0; width: 100%;
} }
.tab-content { .tab-content {
height: 100%; width: 100%;
} }
.list-scroll-content { :deep(.u-tabs),
height: 100%; :deep(.u-tabs__wrapper),
:deep(.u-tabs__wrapper__scroll-view-wrapper),
:deep(.u-tabs__wrapper__scroll-view),
:deep(.u-tabs__wrapper__nav) {
width: 100%; width: 100%;
} }

View File

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

View File

@@ -11,16 +11,16 @@
<view class="point-summary"> <view class="point-summary">
<view class="point-summary-item"> <view class="point-summary-item">
<text>累计获得</text> <text>累计获得</text>
<text class="pcolor">{{ pointData.totalPoint || 0 }}</text> <text class="pcolor">{{ pointSummary.totalPoint || 0 }}</text>
</view> </view>
<view class="point-summary-divider"></view> <view class="point-summary-divider"></view>
<view class="point-summary-item"> <view class="point-summary-item">
<text>剩余积分</text> <text>剩余积分</text>
<text class="pcolor">{{ pointData.point || 0 }}</text> <text class="pcolor">{{ pointSummary.point || 0 }}</text>
</view> </view>
</view> </view>
<div class="point-list"> <view class="point-list">
<view class="point-item" v-for="(item, index) in pointList" :key="index"> <view class="point-item" v-for="(item, index) in pointList" :key="index">
<view class="point-item-left"> <view class="point-item-left">
<view class="point-label">{{ item.content }}</view> <view class="point-label">{{ item.content }}</view>
@@ -30,80 +30,76 @@
<text>{{ item.pointType == "INCREASE" ? "+" : "-" }}</text>{{ item.variablePoint }} <text>{{ item.pointType == "INCREASE" ? "+" : "-" }}</text>{{ item.variablePoint }}
</view> </view>
</view> </view>
<uni-load-more :status="count.loadStatus"></uni-load-more> <uni-load-more :status="loadStatus"></uni-load-more>
</div> </view>
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getPointsData } from "@/api/members.js"; import { ref } from 'vue'
import { getMemberPointSum } from "@/api/members.js"; import { onLoad, onReachBottom } from '@dcloudio/uni-app'
export default { import { useStore } from '@/store'
data() { import { getPointsData, getMemberPointSum } from '@/api/members.js'
return {
count: {
loadStatus: "more",
},
pointList: [], //积分数据集合
params: {
pageNumber: 1,
pageSize: 10,
},
pointData: {}, //累计获取 未输入 集合
};
},
onLoad() { const store = useStore()
this.initPointData();
this.getList();
},
/** const loadStatus = ref('more')
* 触底加载 const pointList = ref<any[]>([])
*/ const params = ref({ pageNumber: 1, pageSize: 10 })
onReachBottom() { const pointSummary = ref<Record<string, any>>({})
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";
}
}
});
},
/** onLoad(() => {
* 获得累计积分使用 fetchPointSummary()
*/ fetchPointLogList()
initPointData() { })
getMemberPointSum().then((res) => {
this.pointData = res.data.result; 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> </script>
<style lang="scss" scoped> <style lang="scss">
page,
.content {
min-height: 100vh;
background: #f9f9f9;
}
.content {
overflow: hidden;
}
.point-list { .point-list {
margin-top: 20rpx; margin-top: 20rpx;
background: #f9f9f9;
min-height: calc(100vh - 390rpx);
padding-bottom: 24rpx;
box-sizing: border-box;
} }
.title { .title {
height: 80rpx; height: 80rpx;
@@ -181,10 +177,6 @@ export default {
} }
} }
.content {
background: #f9f9f9;
}
.more { .more {
text-align: right; text-align: right;
color: $u-tips-color; color: $u-tips-color;
@@ -246,4 +238,8 @@ export default {
margin-bottom: 10rpx; margin-bottom: 10rpx;
color: #666666; color: #666666;
} }
.point-list .uni-load-more {
background: #f9f9f9;
}
</style> </style>

View File

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

View File

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

View File

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

View File

@@ -1,227 +1,211 @@
<template> <template>
<view class="box"> <view class="box">
<view class="box-tips"> <view class="box-tips">
<h2 class='h2'> <h2 class="h2">绑定手机号码</h2>
绑定手机号码
</h2>
<view class="verification"></view> <view class="verification"></view>
</view> </view>
<view class="form"> <view class="form">
<u-form :model="codeForm" ref="validateCodeForm"> <up-form
<view v-if="!validateFlage"> :model="codeForm"
<u-form-item label-width="120" label="手机号" prop="mobile"> 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-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-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code unique-key="page-edit" :seconds="seconds" @end="end" @start="start" <u-code
ref="uCode" @change="codeChange"></u-code> unique-key="page-edit"
<view @tap="getCode" class="text-tips">{{ tips }}</view> :seconds="seconds"
</u-form-item> @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> <view class="submit" @click="submitBindMobile">绑定</view>
<myVerification keep-running @send="verification" class="verification" ref="verification" <myVerification
business="BIND_MOBILE" /> keep-running
@send="onVerificationPassed"
class="verification"
ref="verificationRef"
business="BIND_MOBILE"
/>
</view> </view>
</u-form> </up-form>
</view> </view>
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { import { ref, reactive, watch, getCurrentInstance } from 'vue'
sendMobile, import { onLoad, onReady } from '@dcloudio/uni-app'
bindMobile import { useStore } from '@/store'
} from "@/api/login"; import { sendMobile, bindMobile } from '@/api/login'
import { getUserInfo } from "@/api/members.js"; import { getUserInfo } from '@/api/members.js'
import storage from "@/utils/storage.js"; import storage from '@/utils/storage.js'
import MyVerification from '@/components/verification/verification.vue'
import myVerification from "@/components/verification/verification.vue"; //验证 const store = useStore()
import uuid from "@/utils/uuid.modified.js"; const { proxy } = getCurrentInstance()!
export default {
components: { const phoneVerified = ref(false)
myVerification, 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 { code: [
uuid, {
validateFlage: false, //是否进行了手机号验证 min: 4,
step: 0, //当前验证步骤 max: 6,
flage: false, //是否验证码验证 required: true,
codeForm: { message: '请输入验证码',
mobile: "", //手机号 trigger: ['blur'],
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("请倒计时结束后再发送");
}
}
},
}, },
],
}
methods: { onLoad((options) => {
// 验证码验证 codeForm.username = options?.username || ''
verification(val) { })
this.flage = val == this.$store.state.verificationKey ? true : false;
},
// 验证手机号 onReady(() => {
validatePhone() { validateCodeForm.value?.setRules(codeRules)
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);
});
}
});
}
});
},
codeChange(text) { watch(verificationPassed, (val) => {
this.tips = text; if (!val) return
}, if (!uCodeRef.value?.canGetCode) {
end() { proxy.$u.toast('请倒计时结束后再发送')
return
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;
} }
uni.showLoading({ title: '正在获取验证码' })
.sendCode { sendMobile(codeForm.mobile, 'BIND_MOBILE').then((res) => {
::v-deep .u-form-item--right__content__slot { if (store.state.isShowToast) uni.hideLoading()
display: flex; 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 { function onVerificationPassed(val: string) {
font-size: 40rpx; verificationPassed.value = val === store.state.verificationKey
font-weight: bold; }
}
page { function submitBindMobile() {
background: #fff; 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 { function onCodeTextChange(text: string) {
padding: 80rpx 0; codeTips.value = text
border-radius: 20rpx; }
}
.submit { function onCodeCountdownEnd() {
background: $light-color; verificationPassed.value = false
} verificationRef.value?.getCode()
}
.box-tips { function requestSmsCode() {
margin: 0 72rpx; 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 { function onCodeCountdownStart() {
font-size: 24rpx; proxy.$u.toast('验证码已发送')
color: #999; verificationPassed.value = true
margin-top: 10rpx; 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> </style>

View File

@@ -1,160 +1,99 @@
<template> <template>
<view class="box"> <view class="box">
<view class="box-tips"> <view class="box-tips">
<h2 class='h2'> <h2 class="h2">{{ verificationTitle.title }}</h2>
{{verificationTitle.title}} <view class="verification">{{ verificationTitle.desc }}</view>
</h2>
<view class="verification">{{verificationTitle.desc}}</view>
</view> </view>
<view class="form"> <view class="form">
<u-form :model="codeForm" ref="validateCodeForm"> <up-form
<u-form-item label-width="120" label="旧密码"> :model="codeForm"
<u-input type="password" v-model="oldPassword" placeholder="请输入您的旧密码" /> ref="validateCodeForm"
</u-form-item> 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="密码"> <up-form-item label-width="180rpx" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" /> <u-input type="password" v-model="password" placeholder="请输入您的密码" />
</u-form-item> </up-form-item>
<u-form-item label-width="120" label="确认密码"> <up-form-item label-width="180rpx" label="确认密码">
<u-input type="password" v-model="newPassword" placeholder="请再次输入您的密码" /> <u-input type="password" v-model="confirmPassword" placeholder="请再次输入您的密码" />
</u-form-item> </up-form-item>
<view class="submit" @click="updatePassword">修改密码</view> <view class="submit" @click="updatePassword">修改密码</view>
</u-form> </up-form>
</view> </view>
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { import { ref, reactive } from 'vue'
resetByMobile, import { modifyPass } from '@/api/login'
modifyPass import { md5 } from '@/utils/md5.js'
} from "@/api/login";
import { const verificationTitle = {
md5 title: '修改密码',
} from "@/utils/md5.js"; // md5 desc: '请验证并输入密码',
import myVerification from "@/components/verification/verification.vue"; //验证 }
import uuid from "@/utils/uuid.modified.js"; const codeForm = reactive({ mobile: '', code: '' })
export default { const oldPassword = ref('')
components: { const password = ref('')
myVerification, const confirmPassword = ref('')
},
data() {
return {
uuid,
validateFlage: false, //是否进行了手机号验证
verificationTitle: {
title: "修改密码",
desc: "请验证并输入密码",
},
step: 0, //当前验证步骤
flage: false, //是否验证码验证
codeForm: { function updatePassword() {
mobile: "", //手机号 if (password.value !== confirmPassword.value) {
code: "", //验证码 uni.showToast({ title: '两次输入密码不一致!', icon: 'none' })
}, return
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;
} }
modifyPass({
.sendCode { password: md5(oldPassword.value),
::v-deep .u-form-item--right__content__slot { newPassword: md5(password.value),
display: flex; }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '修改成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
} }
} })
}
</script>
.h2 { <style lang="scss" scoped>
font-size: 40rpx; @import url("@/pages/passport/login.scss");
font-weight: bold;
}
page { ::v-deep .u-form-item {
background: #fff; margin: 40rpx 0;
} }
.box { .h2 {
padding: 80rpx 0; font-size: 40rpx;
border-radius: 20rpx; font-weight: bold;
} }
.submit { page {
background: $light-color; background: #fff;
} }
.box-tips { .box {
margin: 0 72rpx; padding: 80rpx 0;
} border-radius: 20rpx;
}
.verification { .submit {
font-size: 24rpx; background: $light-color;
color: #999; }
margin-top: 10rpx;
} .box-tips {
margin: 0 72rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style> </style>

View File

@@ -1,281 +1,238 @@
<template> <template>
<view class="box"> <view class="box">
<view class="box-tips"> <view class="box-tips">
<h2 class='h2'> <h2 class="h2">{{ verificationTitle[phoneVerified ? 1 : 0].title }}</h2>
{{verificationTitle[validateFlage==false ? 0 : 1].title}} <view class="verification">{{ verificationTitle[step].desc }}</view>
</h2>
<view class="verification">{{verificationTitle[step].desc}}</view>
</view> </view>
<view class="form"> <view class="form">
<u-form :model="codeForm" ref="validateCodeForm"> <up-form
<view v-if="!validateFlage"> :model="codeForm"
<u-form-item label-width="120" label="手机号" prop="mobile"> 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-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-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code unique-key="page-edit" :seconds="seconds" @end="end" @start="start" <u-code
ref="uCode" @change="codeChange"></u-code> unique-key="page-edit"
<view @tap="getCode" class="text-tips">{{ tips }}</view> :seconds="seconds"
</u-form-item> @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> <view class="submit" @click="verifyMobile">验证</view>
<myVerification keep-running @send="verification" class="verification" ref="verification" <myVerification
business="FIND_USER" /> keep-running
@send="onVerificationPassed"
class="verification"
ref="verificationRef"
business="FIND_USER"
/>
</view> </view>
<view v-if="validateFlage"> <view v-if="phoneVerified">
<u-form-item label-width="120" label="密码"> <up-form-item label-width="180rpx" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" /> <u-input type="password" v-model="password" placeholder="请输入您的密码" />
</u-form-item> </up-form-item>
<u-form-item label-width="120" label="确认密码"> <up-form-item label-width="180rpx" label="确认密码">
<u-input type="password" v-model="newPassword" placeholder="请再次输入您的密码" /> <u-input type="password" v-model="confirmPassword" placeholder="请再次输入您的密码" />
</u-form-item> </up-form-item>
<view class="submit" @click="updatePassword">修改密码</view> <view class="submit" @click="updatePassword">修改密码</view>
</view> </view>
</u-form> </up-form>
</view> </view>
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { import { ref, reactive, watch, getCurrentInstance } from 'vue'
sendMobile, import { onReady } from '@dcloudio/uni-app'
resetByMobile, import { useStore } from '@/store'
resetPassword import { isLogin } from '@/utils/filters.js'
} from "@/api/login"; import { sendMobile, resetByMobile, resetPassword } from '@/api/login'
import { md5 } from '@/utils/md5.js'
import MyVerification from '@/components/verification/verification.vue'
import { const store = useStore()
md5 const { proxy } = getCurrentInstance()!
} from "@/utils/md5.js"; // md5
import myVerification from "@/components/verification/verification.vue"; //验证 const phoneVerified = ref(false)
import uuid from "@/utils/uuid.modified.js"; const verificationPassed = ref(false)
export default { const verificationTitle = [
components: { { title: '安全验证', desc: '请输入当前手机号进行安全验证' },
myVerification, { 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 { code: [
uuid, {
validateFlage: false, //是否进行了手机号验证 min: 4,
verificationTitle: [{ max: 6,
title: "安全验证", required: true,
desc: "请输入当前手机号进行安全验证", message: '请输入验证码',
}, trigger: ['blur'],
{
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"],
}, ],
},
};
}, },
onReady() { ],
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕 }
this.$refs.validateCodeForm.setRules(this.codeRules);
},
watch: {
flage(val) {
if (val) {
if (this.$refs.uCode.canGetCode) { onReady(() => {
uni.showLoading({ validateCodeForm.value?.setRules(codeRules)
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("请倒计时结束后再发送");
}
}
},
},
methods: { watch(verificationPassed, (val) => {
// 修改密码 if (!val) return
updatePassword() { if (!uCodeRef.value?.canGetCode) {
if(this.password !== this.newPassword){ proxy.$u.toast('请倒计时结束后再发送')
uni.showToast({ return
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;
} }
uni.showLoading({ title: '正在获取验证码' })
.sendCode { sendMobile(codeForm.mobile, 'FIND_USER').then((res) => {
::v-deep .u-form-item--right__content__slot { if (store.state.isShowToast) uni.hideLoading()
display: flex; 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 { function onVerificationPassed(val: string) {
font-size: 40rpx; verificationPassed.value = val === store.state.verificationKey
font-weight: bold; }
}
page { function updatePassword() {
background: #fff; 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 { function verifyMobile() {
padding: 80rpx 0; validateCodeForm.value?.validate((valid: boolean) => {
border-radius: 20rpx; if (!valid) return
} resetByMobile(codeForm).then((res) => {
if (res.data.success) {
phoneVerified.value = true
uni.showToast({ title: '验证成功!', icon: 'none' })
}
})
})
}
.submit { function onCodeTextChange(text: string) {
background: $light-color; codeTips.value = text
} }
.box-tips { function onCodeCountdownEnd() {
margin: 0 72rpx; verificationPassed.value = false
} verificationRef.value?.getCode()
}
.verification { function isCurrentUserPhone() {
font-size: 24rpx; const user = isLogin()
color: #999; if (user?.mobile !== codeForm.mobile) {
margin-top: 10rpx; 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> </style>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,10 +3,10 @@
<view class="date-card"> <view class="date-card">
<div class="box"> <div class="box">
<div class="circle-box"> <div class="circle-box">
<div class="cricle" @click="signIn()"> <div class="cricle" @click="handleSignIn()">
<span v-if="!ifSign" :class="{ active: signFlag || ifSign }">签到</span> <span v-if="!hasSignedToday" :class="{ active: signAnimating || hasSignedToday }">签到</span>
<span v-else :class="{ active: signFlag || ifSign }" <span v-else :class="{ active: signAnimating || hasSignedToday }"
:style="ifSign ? 'transform: rotateY(0deg);' : ''">已签</span> :style="hasSignedToday ? 'transform: rotateY(0deg);' : ''">已签</span>
</div> </div>
</div> </div>
<text class="tips">坚持每天连续签到可以获多重奖励哦</text> <text class="tips">坚持每天连续签到可以获多重奖励哦</text>
@@ -22,19 +22,19 @@
<view class="week"> <view class="week">
<text v-for="item in weekArr" :key="item.id">{{ item }}</text> <text v-for="item in weekArr" :key="item.id">{{ item }}</text>
</view> </view>
<view class="date" v-for="obj in dataObj" :key="obj.id"> <view class="date" v-for="(obj, rowIndex) in calendarRows" :key="rowIndex">
<view class="item" v-for="item in obj" :key="item.id" :class="item == '' ? 'hide' : ''" <view class="item" v-for="(item, dayIndex) in obj" :key="dayIndex" :class="item == '' ? 'hide' : ''"
:animation="item == currentDay ? animationData : ''"> :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="top">{{ item }} </view>
<view class="bottom"> <view class="bottom">
<u-icon name="error" v-if="item <= currentDay" size="24" color="#999"></u-icon> <u-icon name="error" v-if="item <= currentDay" size="24" color="#999"></u-icon>
</view> </view>
</view> </view>
<view class="back" :class="signArr.indexOf(item) != -1 ? 'active' : ''" :style=" <view class="back" :class="signedDays.indexOf(item) != -1 ? 'active' : ''" :style="
signArr.indexOf(item) != -1 && ifSign signedDays.indexOf(item) != -1 && hasSignedToday
? 'transform: rotateY(0deg);' ? 'transform: rotateY(0deg);'
: signArr.indexOf(item) != -1 && item != currentDay : signedDays.indexOf(item) != -1 && item != currentDay
? 'transform: rotateY(0deg);' ? 'transform: rotateY(0deg);'
: '' : ''
"> ">
@@ -47,11 +47,11 @@
</view> </view>
</view> </view>
</div> </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"> <view class="mask-header">
<text class="close"></text> <text class="close"></text>
<text>签到成功</text> <text>签到成功</text>
<text class="close" @click="close">×</text> <text class="close" @click="closeSuccessMask">×</text>
</view> </view>
<view class="mask-con"> <view class="mask-con">
<u-icon size="120" style="margin: 50rpx 0" :color="aiderLightColor" name="checkmark"></u-icon> <u-icon size="120" style="margin: 50rpx 0" :color="aiderLightColor" name="checkmark"></u-icon>
@@ -61,231 +61,147 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { sign, signTime } from "@/api/point.js"; import { ref, computed } from 'vue'
export default { import { onLoad } from '@dcloudio/uni-app'
data() { import { useStore } from '@/store'
return { import { sign, signTime } from '@/api/point.js'
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;
}
},
/** const store = useStore()
* 点击签到 const aiderLightColor = computed(() => store.getters.aiderLightColor)
*/
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",
});
return false; const signAnimating = ref(false)
} const animationData = ref({})
var that = this; const showSuccessMask = ref(false)
var animation = uni.createAnimation({ const maskClosing = ref(false)
duration: 200, const weekArr = ['日', '一', '二', '三', '四', '五', '六']
timingFunction: "linear", const monthLabels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
}); const currentMonth = ref('')
this.signArr.push(this.currentDay); const currentMonthIndex = ref(0)
this.animation = animation; const currentYear = ref(0)
animation.rotateY(0).step(); const currentDay = ref(0)
this.animationData = animation.export(); const calendarRows = ref<any[][]>([])
const signedDays = ref<number[]>([])
const signRecords = ref<any[]>([])
const hasSignedToday = ref(false)
setTimeout( onLoad(async () => {
function () { const response = await signTime(
that.signFlag = true; `${new Date().getFullYear()}${padZero(new Date().getMonth() + 1)}`
this.maskFlag = true; )
this.ifSign = !this.ifSign; signRecords.value = response.data.result
animation.rotateY(0).step(); buildCalendar()
this.animationData = animation.export(); })
}.bind(this),
200
);
});
},
/** function padZero(val: number) {
* 签到成功后关闭弹窗 return val >= 10 ? String(val) : `0${val}`
*/ }
close() {
var that = this;
this.maskFlag = false;
this.transFlag = true;
setTimeout(() => {
that.transFlag = false;
}, 500);
},
/** async function handleSignIn() {
* 获取今天时间 if (hasSignedToday.value || signAnimating.value) return
*
*/
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();
},
/** const response = await sign()
* 获取当前已经签到的时间 if (response.data.code !== 200) {
*/ uni.showToast({
curentSignData() { title: response.data.message,
var date = new Date(), duration: 2000,
index = date.getMonth(), icon: 'none',
curDay = null; })
this.signArr = []; return
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 animation = uni.createAnimation({ duration: 200, timingFunction: 'linear' })
* 循环出当前月份的时间 signedDays.value.push(currentDay.value)
* 例子: animation.rotateY(0).step()
* "","","","","","",1, animationData.value = animation.export()
* 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);
},
/** setTimeout(() => {
* 获取当前月份有几周 signAnimating.value = true
*/ showSuccessMask.value = true
getWeekByDay(dayValue) { hasSignedToday.value = true
var day = new Date(Date.parse(dayValue.replace(/-/g, "/"))).getDay(); //将日期值格式化 animation.rotateY(0).step()
return day; 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> </script>
<style scoped> <style scoped>
page { page {

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@
:scrollable="false" :scrollable="false"
v-model:current="current" v-model:current="current"
@change="change" @change="change"
:lineColor="$lightColor" :lineColor="lightColor"
:activeStyle="{ color: $lightColor }" :activeStyle="{ color: lightColor }"
></u-tabs> ></u-tabs>
</view> </view>
<div class="u-tabs-search"> <div class="u-tabs-search">
@@ -194,279 +194,199 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import uniLoadMore from "@/components/uni-load-more/uni-load-more.vue"; import { ref, reactive, computed } from 'vue'
import { getAfterSaleList, cancelAfterSale } from "@/api/after-sale.js"; import { onLoad, onPullDownRefresh } from '@dcloudio/uni-app'
import { getOrderList } from "@/api/order.js"; import { useStore } from '@/store'
import storage from "@/utils/storage"; 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 { const store = useStore()
components: { const lightColor = computed(() => store.getters.lightColor)
uniLoadMore,
}, const list = [
data() { { name: '售后申请' },
return { { name: '申请中' },
list: [ { name: '申请记录' },
//tab表头 ]
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: "申请中", })
}, orderList.value = orderList.value.concat(afterSaleLogList)
{ status.value = afterSaleLogList.length < 10 ? 'nomore' : 'loading'
name: "申请记录", })
}, }
],
current: 0, //当前表头索引
tipsShow: false, //提示开关
cancelShow: false, //取消显示开关
selectedOrder: "", //选中的order
orderList: [], //订单集合
params: {
pageNumber: 1,
pageSize: 10,
sort: "createTime",
flowPrice: 0,
order: "desc",
},
logParams: { function applyService(sn: string, order: any, sku: any) {
pageNumber: 1, storage.setAfterSaleData({ ...order, ...sku })
pageSize: 10, uni.navigateTo({ url: `/pages/order/afterSales/afterSalesSelect?sn=${sn}` })
}, }
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 onExpress(order: any, sku: any) {
* 搜索初始化 sku.storeName = order.storeName
* 根据当前tab传值的索引进行更改 storage.setAfterSaleData({ ...order, ...sku })
*/ uni.navigateTo({ url: `./afterSalesDetailExpress?serviceSn=${order.sn}` })
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 onDetail(goods: any, sku: any) {
* 获取订单列表 if (current.value == 0) {
*/ uni.navigateTo({
getOrderList() { url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId || sku.goodsId}`,
uni.showLoading({ })
title: "加载中", } else {
mask: true, uni.navigateTo({
}); url: `/pages/product/goods?id=${goods.skuId}&goodsId=${goods.goodsId || goods.goodsId}`,
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";
}
});
},
close(order, sku) { function renderDate() {
console.log(order, sku); if (current.value === 0) {
this.selectedOrder = order; params.pageNumber += 1
this.cancelShow = true; fetchOrderList()
}, } else {
logParams.pageNumber += 1
async closeService() { fetchAfterSaleLogList()
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();
}
},
},
};
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@@ -1,7 +1,12 @@
<template> <template>
<view class="page-wrap"> <view class="page-wrap">
<scroll-view scroll-y class="page-scroll"> <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="after-sales-goods-detail-view">
<view class="header"> <view class="header">
<view> <view>
@@ -58,7 +63,7 @@
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon> <u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</view> </view>
</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 <u-input
v-model="form.problemDesc" v-model="form.problemDesc"
type="textarea" type="textarea"
@@ -67,54 +72,54 @@
height="120" height="120"
placeholder="请描述申请售后的说明" placeholder="请描述申请售后的说明"
/> />
</u-form-item> </up-form-item>
</view> </view>
<!-- 退款方式 / 银行信息 --> <!-- 退款方式 / 银行信息 -->
<view class="opt-view form-block"> <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">{{ <view class="form-value">{{
applyInfo.refundWay == 'ORIGINAL' ? '原路退回' : '账号退款' applyInfo.refundWay == 'ORIGINAL' ? '原路退回' : '账号退款'
}}</view> }}</view>
</u-form-item> </up-form-item>
<template v-if=" <template v-if="
applyInfo.accountType === 'BANK_TRANSFER' && applyInfo.accountType === 'BANK_TRANSFER' &&
applyInfo.applyRefundPrice != 0 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 <u-input
v-model="form.bankDepositName" v-model="form.bankDepositName"
border="none" border="none"
input-align="right" input-align="right"
placeholder="请输入银行开户行" 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">
<u-input <u-input
v-model="form.bankAccountName" v-model="form.bankAccountName"
border="none" border="none"
input-align="right" input-align="right"
placeholder="请输入银行开户名" placeholder="请输入银行开户名"
/> />
</u-form-item> </up-form-item>
<u-form-item label="银行账号" label-width="150" :border-bottom="true" class="bank-account-item"> <up-form-item label="银行账号" label-width="180rpx" :border-bottom="true" class="bank-account-item">
<u-input <u-input
v-model="form.bankAccountNumber" v-model="form.bankAccountNumber"
border="none" border="none"
input-align="right" input-align="right"
placeholder="请输入银行账号" placeholder="请输入银行账号"
/> />
</u-form-item> </up-form-item>
</template> </template>
<u-form-item <up-form-item
v-if="form.serviceType !== 'RETURN_MONEY'" v-if="form.serviceType !== 'RETURN_MONEY'"
label="返回方式" label="返回方式"
label-width="150" label-width="180rpx"
:border-bottom="false" :border-bottom="false"
> >
<view class="form-value">快递至第三方卖家</view> <view class="form-value">快递至第三方卖家</view>
</u-form-item> </up-form-item>
</view> </view>
<!-- 上传凭证 --> <!-- 上传凭证 -->
@@ -133,7 +138,7 @@
<view class="opt-tip">提交服务单后,售后专员可能与您电话沟通,请保持手机畅通</view> <view class="opt-tip">提交服务单后,售后专员可能与您电话沟通,请保持手机畅通</view>
</view> </view>
</u-form> </up-form>
</scroll-view> </scroll-view>
<view class="submit-view"> <view class="submit-view">
@@ -142,7 +147,7 @@
ripple ripple
shape="circle" shape="circle"
v-if="applyInfo.refundWay" v-if="applyInfo.refundWay"
:custom-style="{ backgroundColor: $lightColor, width: '100%' }" :custom-style="{ backgroundColor: lightColor, width: '100%' }"
@click="onSubmit" @click="onSubmit"
>提交申请</u-button> >提交申请</u-button>
</view> </view>
@@ -158,246 +163,214 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { import { ref, reactive, computed, getCurrentInstance } from 'vue'
getAfterSaleReason, import { onLoad } from '@dcloudio/uni-app'
applyReturn, import { useStore } from '@/store'
getAfterSaleInfo, import { unitPrice, parseGoodsImageUrl } from '@/utils/filters.js'
} from "@/api/after-sale"; 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"; const store = useStore()
import { handleUploadAfterRead } from "@/utils/uploadHelper.js"; const { proxy } = getCurrentInstance()!
import storage from "@/utils/storage.js"; const $u = proxy!.$u
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 lightColor = computed(() => store.getters.lightColor)
* 判断当前内容并生成数据
*/ const fileList = ref<any[]>([])
onLoad(options) { const sn = ref('')
let navTitle = "申请售后"; const sku = ref<any>({})
this.form.serviceType = "RETURN_GOODS"; const reasonSelectShow = ref(false)
if (options.value == 1) { const reasonList = ref<any[]>([])
navTitle = "申请退货"; const applyInfo = ref<any>({})
this.form.serviceType = "RETURN_GOODS"; 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 = "申请换货"; hideLoadingIfNeeded()
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();;
this.form.orderItemSn = options.sn; function init(orderItemSn: string) {
this.form.skuId = this.sku.skuId; getAfterSaleInfo(orderItemSn).then((response) => {
this.form.num = this.sku.num; if (response.data.code == 400) {
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;
}
uni.showToast({ uni.showToast({
title: text, title: response.data.message,
icon: type === "success" ? "success" : "none", duration: 2000,
}); icon: 'none',
}, })
//提交申请 } else {
onSubmit() { applyInfo.value = response.data.result
//提交申请前检测参数 form.accountType = response.data.result.accountType
if (!this.handleCheckParams()) { }
return; })
} }
uni.showLoading({ function openReasonPicker() {
title: "加载中", if (!reasonList.value.length) {
}); uni.showToast({ title: '暂无可选原因', icon: 'none' })
this.form.accountType = this.applyInfo.accountType; return
this.form.refundWay = this.applyInfo.refundWay; }
this.form.applyRefundPrice = this.applyInfo.applyRefundPrice; reasonSelectShow.value = true
}
applyReturn(this.sn, this.form).then((resp) => { function reasonSelectConfirm(val: any) {
if (this.$store.state.isShowToast){ uni.hideLoading() }; const selected = val?.value?.[0] || val?.[0]
if (resp.data.success) { if (selected) {
this.showToast("提交成功", "success"); form.reason = selected.label || selected.text || ''
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;
}
if ( function valChange(e: { value: number }) {
this.applyInfo.accountType === "BANK_TRANSFER" && form.num = e.value
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;
}
}
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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

@@ -57,59 +57,51 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getAfterSaleInfo } from "@/api/after-sale"; import { ref } from 'vue'
import storage from "@/utils/storage"; import { onLoad } from '@dcloudio/uni-app'
export default { import { unitPrice, parseGoodsImageUrl } from '@/utils/filters.js'
data() { import { getAfterSaleInfo } from '@/api/after-sale'
return { import storage from '@/utils/storage'
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;
}
});
},
/** const sn = ref('')
* 选择退货流程 const sku = ref<any>({})
*/ const applyInfo = ref<any>({})
onSelect(value) {
uni.redirectTo({
url: `./afterSalesDetail?sn=${this.sn}&value=${value}`,
});
},
/** onLoad((options) => {
* 跳转到商品信息 sn.value = options?.sn || ''
*/ sku.value = storage.getAfterSaleData()
navigateToGoodsDetail(id) { init()
uni.navigateTo({ })
url: `/pages/product/goods?id=${id}&goodsId=${goodsId}`,
}); 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> </script>
<style lang="scss"> <style lang="scss">

View File

@@ -25,7 +25,7 @@
</view> </view>
<view class="goods-info"> <view class="goods-info">
<view class="info-box"> <view class="info-box">
<view class="goods-item-view" @click="navgiateToGoodsDetail(serviceDetail)"> <view class="goods-item-view" @click="navigateToGoodsDetail(serviceDetail)">
<view class="goods-img"> <view class="goods-img">
<u-image <u-image
border-radius="6" border-radius="6"
@@ -184,198 +184,184 @@
</view> </view>
</template> </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 { import {
getServiceDetail, getServiceDetail,
getStoreAfterSaleAddress, getStoreAfterSaleAddress,
getAfterSaleLog, getAfterSaleLog,
getAfterSaleReason, getAfterSaleReason,
} from "@/api/after-sale.js"; } 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) {},
},
});
},
/** const store = useStore()
* 获取地址信息
*/ const reason = ref('')
getAddress() { const serviceTypeList: Record<string, string> = {
getStoreAfterSaleAddress(this.sn).then((res) => { CANCEL: '取消',
if (res.data.success) { RETURN_GOODS: '退货',
this.storeAfterSaleAddress = res.data.result; 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: () => {},
}, },
})
}
/** function fetchAddress() {
* 获取日志 getStoreAfterSaleAddress(sn.value).then((res) => {
*/ if (res.data.success) {
getLog(sn) { storeAfterSaleAddress.value = res.data.result
getAfterSaleLog(sn).then((res) => { }
this.logs = res.data.result; })
}); }
},
/** function fetchLog(serviceSn: string) {
* 获取申请原因 getAfterSaleLog(serviceSn).then((res) => {
*/ logs.value = res.data.result
getReasonList(serviceType) { })
getAfterSaleReason(serviceType).then((res) => { }
if (res.data.success) {
// 1357583466371219456
this.reason = this.serviceDetail.reason;
}
});
},
/** function fetchReasonList(serviceType: string) {
* 初始化详情 getAfterSaleReason(serviceType).then((res) => {
*/ if (res.data.success) {
loadDetail() { reason.value = serviceDetail.value.reason
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;
}
this.accountShow = function loadDetail() {
(this.serviceDetail.serviceType === "RETURN_GOODS" || uni.showLoading({ title: '加载中' })
this.serviceDetail.serviceType === "ORDER_CANCEL") && getServiceDetail(sn.value).then((res) => {
this.serviceDetail.refundWay === "OFFLINE"; hideLoadingIfNeeded()
serviceDetail.value = res.data.result
if (
serviceDetail.value.serviceType == 'RETURN_GOODS' ||
serviceDetail.value.serviceType === 'RETURN_MONEY'
) {
refundShow.value = true
}
this.bankShow = accountShow.value =
this.serviceDetail.accountType === "BANK_TRANSFER" && (serviceDetail.value.serviceType === 'RETURN_GOODS' ||
this.serviceDetail.refundWay === "OFFLINE" && serviceDetail.value.serviceType === 'ORDER_CANCEL') &&
((this.serviceDetail.serviceType === "RETURN_GOODS") | serviceDetail.value.refundWay === 'OFFLINE'
(this.serviceDetail.serviceType === "ORDER_CANCEL") ||
this.serviceDetail.serviceType === "RETURN_MONEY");
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')
/** fetchReasonList(serviceDetail.value.serviceType)
* 访问商品详情 })
*/ }
navgiateToGoodsDetail(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
/** function navigateToGoodsDetail(item: any) {
* 进度 uni.navigateTo({
*/ url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
onProgress() { })
uni.navigateTo({ }
url: `./applyProgress?sn=${
this.serviceDetail.sn function onProgress() {
}&createTime=${encodeURIComponent(this.serviceDetail.createTime)} uni.navigateTo({
&logs=${encodeURIComponent(JSON.stringify(this.logs))}&serviceStatus=${ url: `./applyProgress?sn=${
this.serviceDetail.serviceStatus serviceDetail.value.sn
}`, }&createTime=${encodeURIComponent(serviceDetail.value.createTime)}
}); &logs=${encodeURIComponent(JSON.stringify(logs.value))}&serviceStatus=${
}, serviceDetail.value.serviceStatus
}, }`,
}; })
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

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

View File

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

View File

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

View File

@@ -51,99 +51,91 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getComplainDetail, communication } from "@/api/after-sale"; import { ref } from 'vue'
export default { import { onLoad } from '@dcloudio/uni-app'
data() { import { useStore } from '@/store'
return { import { getComplainDetail, communication } from '@/api/after-sale'
complainId: "",
complainValue: "", //回复内容
complainDetail: "", //投诉详情
statusData: {
NEW: "新投诉",
NO_APPLY: "未申请",
APPLYING: "申请中",
COMPLETE: "已完成",
EXPIRED: "已失效",
CANCEL: "已取消",
WAIT_ARBITRATION:"等待仲裁"
},
};
},
onLoad(option) { const store = useStore()
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 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) { function handleSubmit() {
uni.showLoading({ if (!complainValue.value) {
title: "加载中", uni.showToast({
}); title: '请输入回复内容',
getComplainDetail(id).then((res) => { duration: 2000,
if (res.data.success) { icon: 'none',
this.complainDetail = res.data.result; })
} else { return
uni.showToast({ }
title: res.data.message, const params = {
duration: 2000, content: complainValue.value,
icon: "none", complainId: complainId.value,
}); }
} communication(params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() }; 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.row { .row {

View File

@@ -10,7 +10,7 @@
<u-line color="#DCDFE6"></u-line> <u-line color="#DCDFE6"></u-line>
<view class="goods-item-view"> <view class="goods-item-view">
<view class="goods-img" @click="handleToGoods(item)"> <view class="goods-img" @click="handleToGoods(item)">
<u-image border-radius="6" width="131rpx" height="131rpx" :src="item.goodsImage"></u-image> <u-image radius="6rpx" width="131rpx" height="131rpx" :src="item.goodsImage"></u-image>
</view> </view>
<view class="goods-info" @click="handleToGoods(item)"> <view class="goods-info" @click="handleToGoods(item)">
<view class="goods-title u-line-2">{{ item.goodsName }}</view> <view class="goods-title u-line-2">{{ item.goodsName }}</view>
@@ -28,9 +28,9 @@
<view class="complain-speak"> {{ item.complainTopic }} </view> <view class="complain-speak"> {{ item.complainTopic }} </view>
</view> </view>
<view class="complain-btn"> <view class="complain-btn">
<u-tag mode="plain" @click="handleClear(item)" class="complain-tag" text="撤销投诉" type="info" <u-tag plain @click="handleClear(item)" class="complain-tag" text="撤销投诉" type="info"
v-if="item.complainStatus === 'APPLYING' || item.complainStatus === 'NEW'" /> v-if="item.complainStatus === 'APPLYING' || item.complainStatus === 'NEW'" />
<u-tag mode="plain" @click="handleInfo(item)" class="complain-tag" text="投诉详情" type="info" /> <u-tag plain @click="handleInfo(item)" class="complain-tag" text="投诉详情" type="info" />
</view> </view>
</view> </view>
@@ -40,114 +40,175 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getComplain, clearComplain } from "@/api/after-sale"; 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'
export default { const store = useStore()
data() {
return { const statusData: Record<string, string> = {
statusData: { NEW: '新投诉',
NEW: "新投诉", NO_APPLY: '未申请',
NO_APPLY: "未申请", APPLYING: '申请中',
APPLYING: "申请中", COMPLETE: '已完成',
COMPLETE: "已完成", EXPIRED: '已失效',
EXPIRED: "已失效", CANCEL: '已取消',
CANCEL: "已取消", WAIT_ARBITRATION: '等待仲裁',
WAIT_ARBITRATION:"等待仲裁" }
},
show: false, const show = ref(false)
content: "是否撤销投诉?", const content = ref('是否撤销投诉?')
params: { const params = ref({ pageNumber: 1, pageSize: 20 })
pageNumber: 1, const complaionDetail = ref<any>(null)
pageSize: 20, const complaionData = ref<any[]>([])
}, const empty = ref(false)
complaionDetail: "", //返回的整个response const checkComplainData = ref<any>(null)
complaionData: [], //投诉列表
empty: false, onLoad(() => {
checkComplainData: "", //存储投诉信息 init()
}; })
},
mounted() { onReachBottom(() => {
this.init(); if (
}, complaionDetail.value &&
/** complaionDetail.value.total > params.value.pageNumber * params.value.pageSize
* 触底加载 ) {
*/ params.value.pageNumber++
onReachBottom() { init()
if ( }
this.complaionDetail && })
this.complaionDetail.total < this.params.pageNumber * this.params.pageSize
) { function handleToGoods(val: any) {
this.params.pageNumber++; uni.navigateTo({
this.init(); 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()
} }
}, })
}
methods: { function handleInfo(val: any) {
// 点击跳转到商品 uni.navigateTo({
handleToGoods(val) { url: './complainInfo?id=' + val.id,
uni.navigateTo({ })
url: "/pages/product/goods?id=" + val.skuId + "&goodsId=" + val.goodsId, }
});
},
/** function hideLoadingIfNeeded() {
* 点击撤销投诉 if (store.state.isShowToast) uni.hideLoading()
*/ }
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();
}
});
},
/** function init() {
* 查看详情 uni.showLoading({
*/ title: '加载中',
handleInfo(val) { })
uni.navigateTo({ getComplain(params.value).then((res) => {
url: "./complainInfo?id=" + val.id, complaionDetail.value = res.data.result
}); if (res.data.result.records.length >= 1) {
}, complaionData.value.push(...res.data.result.records)
} else {
/** empty.value = true
* 初始化投诉列表 }
*/ hideLoadingIfNeeded()
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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../goods.scss"; .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 { .complain-item-view {
border-bottom: 2rpx solid #f5f7fa; border-bottom: 2rpx solid #f5f7fa;
@@ -162,9 +223,6 @@ export default {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #999;
} }
::v-deep .seller-name {
width: auto !important;
}
.complain-btn { .complain-btn {
padding: 20rpx 0; padding: 20rpx 0;
display: flex; display: flex;

View File

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

View File

@@ -31,7 +31,7 @@
<view class="img"> <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" <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> </u-image>
</view> </view>
</view> </view>
@@ -40,56 +40,46 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import configs from '@/config/config' import configs from '@/config/config'
export default {
data() {
return {
configs,
userImage:configs.defaultUserPhoto,
comment: {}, //评论信息 const userImage = configs.defaultUserPhoto
gradeList: {
//评价grade const comment = ref<Record<string, any>>({})
GOOD: "好评", const gradeList: Record<string, string> = {
MODERATE: "中评", GOOD: '好评',
WORSE: "差评", MODERATE: '中评',
haveImage: "有图", WORSE: '差评',
}, haveImage: '有图',
}; }
},
onLoad(options) { onLoad((options) => {
this.comment = JSON.parse(decodeURIComponent(options.comment)); comment.value = JSON.parse(decodeURIComponent(options.comment))
}, })
methods: {
/** function splitImg(val: string) {
* 切割图像 if (val && val.split(',')) {
*/ return val.split(',')
splitImg(val) { } else if (val) {
if (val && val.split(",")) { return val
return val.split(","); }
} else if (val) { return false
return val; }
} else {
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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -8,8 +8,9 @@
:inactiveStyle="{ color: '#333' }" :inactiveStyle="{ color: '#333' }"
v-model:current="current" v-model:current="current"
class="utabs" class="utabs"
:lineColor="$lightColor" :lineColor="lightColor"
:activeStyle="{ color: $lightColor }" :activeStyle="{ color: lightColor }"
:bg-color="'#ffffff'"
></u-tabs> ></u-tabs>
</view> </view>
<swiper class="swiper-box" :current="current" @change="changeSwiper" duration="500"> <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="btn-view u-row-between" v-if="current == 2">
<view class="description"> <view class="description">
<view class="text title"> <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> <rich-text :nodes="'评论内容:' + order.content || ''"></rich-text>
</u-read-more> </u-read-more>
</view> </view>
@@ -78,209 +79,144 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getOrderList } from "@/api/order.js"; import { ref, reactive, computed, watch } from 'vue'
import { getComments } from "@/api/members.js"; import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getOrderList } from '@/api/order.js'
import { getComments } from '@/api/members.js'
export default { const store = useStore()
data() { const lightColor = computed(() => store.getters.lightColor)
return {
list: [
//顶部tab
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: "待评价", })
}, orderList.value = orderList.value.concat(records)
{ params.pageNumber += 1
name: "已评价", })
}, }
],
gradeList: {
//评论表
GOOD: "好评",
MODERATE: "中评",
WORSE: "差评",
haveImage: "有图",
},
groupCommentStatusWay: {
NEW: "新订单,不能进行评论",
UNFINISHED: "未完成评论",
WAIT_CHASE: "待追评的评论信息",
FINISHED: "已经完成评论",
},
current: 0, //当前tabIndex
orderList: [], //商品集合
params: {
pageNumber: 1,
pageSize: 10,
loadStatus: "more",
},
};
},
onShow() { function renderData(index: number) {
this.orderList = []; if (params.loadStatus == 'noMore') return
this.params.pageNumber = 1; if (index == 0) {
this.current = 0 loadData()
this.loadData() } else {
}, loadComments()
watch: { }
/** }
* 切换current
* 更改页面并重新加载数据
*/
current(val) {
this.params.pageNumber = 1;
this.params.loadStatus = "more";
this.orderList = [];
//重新读取数据
if (val == 0) { function onDetail(comment: any) {
delete this.params.commentStatus uni.navigateTo({
this.loadData(); url: './evaluateDetail?comment=' + encodeURIComponent(JSON.stringify(comment)),
} 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)),
});
},
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
page { page {
@@ -310,6 +246,7 @@ page {
.u-tabs-box { .u-tabs-box {
position: relative; position: relative;
z-index: 10; z-index: 10;
background: #ffffff;
} }
.box-content { .box-content {
margin: 20rpx 0; margin: 20rpx 0;

View File

@@ -86,80 +86,69 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import storage from "@/utils/storage.js"; import { ref, reactive } from 'vue'
import { commentsMemberOrder } from "@/api/members.js"; import { onLoad } from '@dcloudio/uni-app'
import { handleUploadAfterRead } from "@/utils/uploadHelper.js"; import { useStore } from '@/store'
import { commentsMemberOrder } from '@/api/members.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
export default { const store = useStore()
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 type = 'textarea'
* 提交评价 const border = false
*/ const maxlength = 500
onSubmit() { const placeholder =
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);
},
});
}
});
},
onUploadAfterRead(event) { const sku = ref<Record<string, any>>({})
handleUploadAfterRead(event, this.uploadFileList, (urls) => { const form = reactive<Record<string, any>>({
this.form.images = urls; 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -384,471 +384,426 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import * as API_Address from "@/api/address"; import { ref, computed, watch, getCurrentInstance } from 'vue'
import * as API_Order from "@/api/order"; import { onLoad, onShow, onBackPress } from '@dcloudio/uni-app'
import * as API_Trade from "@/api/trade"; import { useStore } from '@/store'
import configs from "@/config/config"; import * as API_Address from '@/api/address'
import LiLiWXPay from "@/js_sdk/lili-pay/wx-pay.js"; import * as API_Order from '@/api/order'
import invoices from "@/pages/order/invoice/setInvoice"; import * as API_Trade from '@/api/trade'
import { mapState } from "vuex"; import configs from '@/config/config'
export default { import LiLiWXPay from '@/js_sdk/lili-pay/wx-pay.js'
onLoad: function (val) { import invoices from '@/pages/order/invoice/setInvoice'
this.routerVal = val; import {
}, unitPrice,
components: { goodsFormatPrice,
invoices, secrecyMobile,
}, isLogin,
} from '@/utils/filters.js'
data() { const store = useStore()
return { const { proxy } = getCurrentInstance()!
configs, const lightColor = computed(() => store.getters.lightColor)
userImage: configs.defaultUserPhoto, const mainColor = computed(() => store.getters.mainColor)
invoiceFlag: false, //开票开关 const aiderLightColor = computed(() => store.getters.aiderLightColor)
shippingText: "LOGISTICS", const remark = computed(() => store.state.remark)
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: "",
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: { { immediate: true, deep: true }
// 监听备注 并在 vuex 中存储 )
remarkVal: {
handler(val) { onLoad((val) => {
this.$store.commit("setRemark", val); routerVal.value = val || {}
}, })
immediate: true,
deep: true, onBackPress((e) => {
}, if (e.from == 'backbutton') {
}, const curRoute = getCurrentPages().slice(-1)[0]?.options || {}
computed: { if (curRoute.addId) {
...mapState(["remark"]), uni.reLaunch({
}, url: '/pages/tabbar/cart/cartList',
/** })
* 监听返回 } else if (routerVal.value?.way === 'CART') {
*/ uni.switchTab({
onBackPress(e) { url: '/pages/tabbar/cart/cartList',
if (e.from == "backbutton") { })
const curRoute = getCurrentPages().slice(-1)[0]?.options || {}; } else {
if (curRoute.addId) { uni.navigateBack()
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;
} }
}, return true
}
return false
})
async onShow() { onShow(async () => {
// 判断是否存在写过备注信息的商品 if (remark.value && remark.value.length > 0) {
if (this.remark && this.remark.length > 0) { remarkFlag.value = true
this.remarkFlag = 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({ } finally {
mask: true, uni.hideLoading()
}); }
try { })
await this.getOrderList();
await this.getDistribution(); function getShippingLabel() {
if (this.routerVal.way == "PINTUAN") { const item =
this.isAssemble = true; shippingMethod.value.find((e) => e.value === shippingText.value) ||
this.routerVal.parentOrder = JSON.parse( shippingWay.find((e) => e.value === shippingText.value)
decodeURIComponent(this.routerVal.parentOrder) return item ? item.label : ''
); }
this.pintuanWay();
} async function callbackInvoice(val: any) {
} finally { invoiceFlag.value = false
uni.hideLoading(); receiptList.value = val
if (val) {
const submit = {
way: routerVal.value.way,
...receiptList.value,
} }
}, const receipt = await API_Order.getReceipt(submit)
mounted() {}, if (receipt.data.success) {
shippingFlag.value = false
getOrderList()
}
}
}
methods: { function navigateToStore(val: any) {
getShippingLabel() { uni.navigateTo({
const item = url: '/pages/product/shopPage?id=' + val.storeId,
this.shippingMethod.find((e) => e.value === this.shippingText) || })
this.shippingWay.find((e) => e.value === this.shippingText); }
return item ? item.label : "";
},
//发票回调 选择发票之后刷新购物车 function clickToAddress() {
async callbackInvoice(val) { navigateTo(
this.invoiceFlag = false; `/pages/mine/address/address?from=cart&way=${
this.receiptList = val; routerVal.value.way
if (val) { }&parentOrder=${encodeURIComponent(
let submit = { JSON.stringify(routerVal.value.parentOrder)
way: this.routerVal.way, )}`
...this.receiptList, )
}; }
let receipt = await API_Order.getReceipt(submit);
if (receipt.data.success) { function clickToStoreAddress() {
this.shippingFlag = false; navigateTo(
this.getOrderList(); `/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
} }
}, } else if (
shippingText.value === 'LOGISTICS' &&
// 跳转到店铺 orderMessage.value.cartTypeEnum !== 'VIRTUAL'
navigateToStore(val) { ) {
uni.navigateTo({ if (!address.value.id) {
url: "/pages/product/shopPage?id=" + val.storeId, uni.showToast({
}); title: '请选择地址',
}, duration: 2000,
// 点击跳转地址 icon: 'none',
clickToAddress() { })
this.navigateTo( return false
`/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;
} }
}, }
// 判断发票
invoice() {
this.invoiceFlag = true;
},
// 领取优惠券 let client
GET_Discount() { // #ifdef H5
// 循环店铺id,商品id获取优惠券 client = 'H5'
let store = []; // #endif
let skus = []; // #ifdef MP-WEIXIN
let selectedCoupon = []; client = 'WECHAT_MP'
if (this.orderMessage.platformCoupon) // #endif
selectedCoupon.push(this.orderMessage.platformCoupon.memberCoupon.id); // #ifdef APP-PLUS
if ( client = 'APP'
this.orderMessage.storeCoupons && // #endif
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}`
);
},
/** const submit: Record<string, any> = {
* 跳转 client,
*/ way: routerVal.value.way,
navigateTo(url) { remark: remarkVal.value,
uni.navigateTo({ parentOrderSn: '',
url, }
}); if (routerVal.value.parentOrder && routerVal.value.parentOrder.orderSn) {
}, submit.parentOrderSn = routerVal.value.parentOrder.orderSn
} else {
delete submit.parentOrderSn
}
/** API_Trade.createTrade(submit).then((res) => {
* 提交订单准备支付
*/
// 创建订单
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;
if (res.data.success) { if (res.data.success) {
this.getOrderList(); uni.showToast({
} title: '创建订单成功!',
}, duration: 2000,
icon: 'none',
// 获取结算参数 })
getOrderList() { if (orderMessage.value.priceDetailDTO.billPrice == 0) {
this.notSupportFreight = []; uni.navigateTo({
this.notSupportFreightNoticeText = ""; url: '/pages/order/myOrder?status=0',
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();
} else { } else {
this.address = res.data.result.memberAddress; // #ifdef MP-WEIXIN
res.data.result.memberAddress.consigneeAddressPath = pay(res.data.result.sn)
res.data.result.memberAddress.consigneeAddressPath.split(","); // #endif
}
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;
});
}
});
},
// // #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> </script>
<style scoped> <style scoped>
page { page {

View File

@@ -74,185 +74,128 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getReceiptDetail } from "@/api/order.js"; import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getReceiptDetail } from '@/api/order.js'
import { unitPrice } from '@/utils/filters.js'
export default { const order = ref<Record<string, any>>({})
data() { const title_type = ref('')
return { const showInvoicePopup = ref(false)
order: {},
title_type: "", onLoad((options) => {
showInvoicePopup: false, loadData(options.id)
}; })
},
onLoad(options) { function loadData(id: string) {
this.loadData(options.id); getReceiptDetail(id).then((res) => {
}, const result = res.data.result
methods: { order.value = result
loadData(id) { title_type.value = result.companyName || result.taxpayerId ? '单位' : '个人'
getReceiptDetail(id).then((res) => { })
let order = res.data.result; }
this.order = order;
this.title_type = order.companyName || order.taxpayerId ? "单位" : "个人"; function getTitleNameValue() {
}); return title_type.value === '单位'
}, ? order.value.companyName || '-'
getTitleNameValue() { : order.value.personalName || '-'
return this.title_type === "单位" }
? this.order.companyName || "-"
: this.order.personalName || "-"; function viewInvoice() {
}, if (!order.value.invoiceAddress) {
viewInvoice() { uni.showToast({
if (!this.order.invoiceAddress) { 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({ uni.showToast({
title: "暂无发票地址", title: '下载失败',
duration: 2000, duration: 2000,
icon: "none", icon: 'none',
}); })
return; return
} }
if (this.isImageInvoice()) { const tempFilePath = res.tempFilePath
this.showInvoicePopup = true; // #ifdef H5
return; const link = document.createElement('a')
} link.href = tempFilePath || order.value.invoiceAddress
// #ifdef APP-PLUS link.download = 'invoice'
plus.runtime.openURL(this.order.invoiceAddress); document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// #endif // #endif
// #ifndef APP-PLUS // #ifndef H5
uni.navigateTo({ uni.saveImageToPhotosAlbum({
url: filePath: tempFilePath,
"/pages/tabbar/home/web-view?src=" + success: () => {
encodeURIComponent(this.order.invoiceAddress), uni.showToast({
}); title: '发票已保存到相册',
// #endif duration: 2000,
}, icon: 'none',
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
}, },
fail: () => { fail: () => {
uni.showToast({ uni.showToast({
title: "下载失败", title: '保存失败',
duration: 2000, duration: 2000,
icon: "none", icon: 'none',
}); })
}, },
}); })
// #endif
}, },
/** fail: () => {
* 点击图片放大或保存 uni.showToast({
*/ title: '下载失败',
preview() { duration: 2000,
//预览发票 icon: 'none',
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",
});
}
}, },
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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -92,340 +92,355 @@
</div> </div>
</u-popup> </u-popup>
</template> </template>
<script> <script setup lang="ts">
export default { import { ref, reactive, computed, watch, onMounted, getCurrentInstance } from 'vue'
props: ["res"],
computed: { interface InvoiceOption {
isSpecialInvoice() { title: string
return this.getActiveTitle(this.invoiceType) === "增值税专用发票"; active: boolean
}, disabled?: boolean
titleName: { }
get() {
return this.isUnitTitle() interface SubmitData {
? this.submitData.companyName receiptTitle: string
: this.submitData.personalName; receiptType: string
}, personalName: string
set(value) { companyName: string
if (this.isUnitTitle()) { taxpayerId: string
this.submitData.companyName = value; receiptContent: string
} else { companyAddress: string
this.submitData.personalName = value; companyPhone: string
} bankName: string
this.syncReceiptTitle(); 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: { set(value: string) {
invoiceType: { if (isUnitTitle()) {
handler(val) { submitData.companyName = value
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();
} else { } else {
this.setActiveByTitle(this.invoiceType, "电子普通发票"); submitData.personalName = value
this.setActiveByTitle(this.invoiceHeader, "个人"); }
this.setActiveByTitle(this.goodsType, "商品明细"); syncReceiptTitle()
this.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: { { deep: true }
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();
if (this.$u.test.isEmpty(receiptTitle)) { watch(
uni.showToast({ invoiceHeader,
title: "请您填写发票抬头!", (val) => {
duration: 2000, if (isSpecialInvoice.value) {
icon: "none", title.value = '单位'
}); taxpayerFlag.value = true
return false; return
} }
if (
!this.$u.test.isEmpty(receiptTitle) &&
this.$u.test.isEmpty(taxpayerId) &&
this.invoiceHeader[1].active == true
) {
uni.showToast({
title: "请您填写纳税人识别号!",
duration: 2000,
icon: "none",
});
return false; title.value = getActiveTitle(val) || '个人'
} taxpayerFlag.value = title.value == '单位'
if (this.isSpecialInvoice && this.$u.test.isEmpty(companyAddress)) { if (!taxpayerFlag.value) {
uni.showToast({ submitData.taxpayerId = ''
title: "请您填写单位地址!", }
duration: 2000, syncReceiptTitle()
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);
},
}, },
}; { 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> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.form-item { .form-item {

View File

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

View File

@@ -51,7 +51,7 @@
<view class="title">自提点地址:</view> <view class="title">自提点地址:</view>
<view class="value address-line-height">{{ order.storeAddressPath }}</view> <view class="value address-line-height">{{ order.storeAddressPath }}</view>
</view> </view>
<view class="order-info-view" @click="callPhone" > <view class="order-info-view" @click="handleCallPhone" >
<view class="title">联系方式:</view> <view class="title">联系方式:</view>
<view class="value">{{ order.storeAddressMobile }}<u-icon name='phone-fill' ></u-icon></view> <view class="value">{{ order.storeAddressMobile }}<u-icon name='phone-fill' ></u-icon></view>
</view> </view>
@@ -245,287 +245,258 @@
</view> </view>
</template> </template>
<script> <script setup lang="ts">
import { getExpress, getPackage } from "@/api/trade.js"; import { ref, computed } from 'vue'
import { cancelOrder, confirmReceipt, getOrderDetail } from "@/api/order.js"; 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 { const logisticsList = ref<any>('')
components: { const shareFlag = ref(false)
shares, const order = ref<Record<string, any>>({})
}, const cancelShow = ref(false)
data() { const orderSn = ref('')
return { const orderGoodsList = ref<any[]>([])
lightColor: this.$lightColor, const orderDetail = ref<Record<string, any>>({})
logisticsList: "", //物流信息 const sn = ref('')
shareFlag: false, //拼团分享开关 const cancelList = ref<any[]>([])
orderStatusMap: { const rogShow = ref(false)
UNPAID: { const reason = ref('')
title: "未付款", const orderPackage = ref<any>('')
value: "商品暂未付款",
}, function hideLoadingIfNeeded() {
PAID: { if (store.state.isShowToast) uni.hideLoading()
title: "已付款", }
value: "买家已付款",
}, onLoad((options) => {
UNDELIVERED: { const orderSnParam = options?.sn || ''
title: "待发货", sn.value = orderSnParam
value: "商品等待发货中", loadData(orderSnParam)
}, })
PARTS_DELIVERED: {
title: "部分发货", function getOrderPackage() {
value: "商品已部分发货。", getPackage(order.value.sn).then((res) => {
}, if (res.data.success) {
DELIVERED: { orderPackage.value = res.data.result
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 "";
} }
}, })
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;
});
},
// 分享当前拼团信息 function handleClickDeliver() {
inviteGroup() { uni.navigateTo({
this.shareFlag = true; url: `/pages/order/deliverDetail?order_sn=${order.value.sn}`,
}, })
// #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 refundPriceList(status: string) {
onReceipt(val) { switch (status) {
uni.navigateTo({ case 'ALL_REFUND':
url: "/pages/order/invoice/invoiceDetail?id=" + val.id, return '全部退款'
}); case 'PART_REFUND':
}, return '部分退款'
gotoGoodsDetail(sku) { case 'NO_REFUND':
uni.navigateTo({ return '未退款'
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId}`, case 'REFUNDING':
}); return '退款中'
}, default:
onCopy(sn) { return ''
setClipboard(sn) }
}, }
//售后按钮 function handleCallPhone() {
onAfterSales(sn, sku) { if (order.value.storeAddressMobile) {
uni.navigateTo({ callPhone(order.value.storeAddressMobile)
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;
uni.showLoading({ function contact(storeId: string) {
title: "加载中", talkIm(storeId)
}); }
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
this.cancelList = res.data.result;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
this.cancelShow = true; function goToShopPage(val: any) {
}, uni.navigateTo({
url: '/pages/product/shopPage?id=' + val.storeId,
})
}
//提交取消订单(未付款) function loadLogistics(orderSnParam: string) {
submitCancel() { getExpress(orderSnParam).then((res) => {
cancelOrder(this.orderSn, { reason: this.reason }).then((res) => { logisticsList.value = res.data.result
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 inviteGroup() {
onRog(sn) { shareFlag.value = true
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 ByUserMessage(orderItem: any) {
reasonChange(reason) { uni.navigateTo({
this.reason = reason; url:
}, '/pages/cart/payment/shareOrderGoods?sn=' +
reBuy(order) { orderItem.sn +
uni.navigateTo({ '&sku=' +
url: orderGoodsList.value[0].skuId +
"/pages/product/goods?id=" + order.id + "&goodsId=" + order.goodsId, '&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) {
setClipboard(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> </script>
<style lang="scss"> <style lang="scss">

View File

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

View File

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