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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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