初始化移动端提交
This commit is contained in:
202
pages/app/democontract/form.vue
Normal file
202
pages/app/democontract/form.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<view class="form-container">
|
||||
<u-form :model="formData" ref="formRef" :rules="rules" label-width="180rpx">
|
||||
<u-form-item label="合同编号" prop="code">
|
||||
<u-input v-model="formData.code" placeholder="请输入合同编号" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同名称" prop="name">
|
||||
<u-input v-model="formData.name" placeholder="请输入合同名称" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同状态" prop="status">
|
||||
<u-picker
|
||||
v-model="formData.status"
|
||||
:range="statusOptions"
|
||||
range-key="text"
|
||||
@change="onStatusChange"
|
||||
>
|
||||
<u-input
|
||||
v-model="statusText"
|
||||
disabled
|
||||
placeholder="请选择合同状态"
|
||||
suffix-icon="arrow-down"
|
||||
/>
|
||||
</u-picker>
|
||||
</u-form-item>
|
||||
<u-form-item label="签订日期" prop="signDate">
|
||||
<uni-datetime-picker v-model="formData.signDate" type="date" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同开始日期" prop="startDate">
|
||||
<uni-datetime-picker v-model="formData.startDate" type="date" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同结束日期" prop="endDate">
|
||||
<uni-datetime-picker v-model="formData.endDate" type="date" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同金额" prop="amount">
|
||||
<u-input v-model="formData.amount" placeholder="请输入合同金额" type="number" />
|
||||
</u-form-item>
|
||||
<u-form-item label="备注" prop="remark">
|
||||
<u-textarea v-model="formData.remark" placeholder="请输入备注" />
|
||||
</u-form-item>
|
||||
<u-form-item label="岗位ID" prop="postId">
|
||||
<u-input v-model="formData.postId" placeholder="请输入岗位ID" />
|
||||
</u-form-item>
|
||||
<!-- TODO: 附件上传 -->
|
||||
</u-form>
|
||||
<u-button
|
||||
type="primary"
|
||||
@click="submit"
|
||||
class="submit-btn"
|
||||
:loading="submitting"
|
||||
:disabled="submitting"
|
||||
>提交</u-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, toRaw } from 'vue'
|
||||
import DemoContractApi from '@/sheep/api/infra/democontract'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
|
||||
const formType = ref('create')
|
||||
const formData = reactive({
|
||||
id: undefined,
|
||||
code: '',
|
||||
name: '',
|
||||
status: 0,
|
||||
signDate: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
amount: undefined,
|
||||
remark: '',
|
||||
postId: ''
|
||||
})
|
||||
|
||||
const statusOptions = [
|
||||
{ value: 0, text: '草稿' },
|
||||
{ value: 1, text: '审核中' },
|
||||
{ value: 2, text: '已通过' },
|
||||
{ value: 3, text: '已拒绝' }
|
||||
]
|
||||
|
||||
const statusText = computed(() => {
|
||||
const option = statusOptions.find(item => item.value === formData.status)
|
||||
return option ? option.text : '请选择合同状态'
|
||||
})
|
||||
|
||||
const rules = {
|
||||
code: [{ required: true, message: '合同编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
|
||||
amount: [{ required: true, message: '合同金额不能为空', trigger: 'blur' }],
|
||||
postId: [{ required: true, message: '岗位ID不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
const submitting = ref(false)
|
||||
|
||||
const formTitle = computed(() => {
|
||||
return formType.value === 'create' ? '新增合同' : '修改合同'
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.type) {
|
||||
formType.value = options.type
|
||||
}
|
||||
if (options.id) {
|
||||
getDemo(options.id)
|
||||
}
|
||||
})
|
||||
|
||||
const getDemo = async (id) => {
|
||||
const res = await DemoContractApi.getDemoContract(id)
|
||||
if (!res || res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
formData.id = data.id
|
||||
formData.code = data.code || ''
|
||||
formData.name = data.name || ''
|
||||
formData.status = Number.isNaN(Number(data.status)) ? 0 : Number(data.status)
|
||||
formData.signDate = data.signDate || ''
|
||||
formData.startDate = data.startDate || ''
|
||||
formData.endDate = data.endDate || ''
|
||||
formData.amount = data.amount !== undefined && data.amount !== null ? String(data.amount) : ''
|
||||
formData.remark = data.remark || ''
|
||||
formData.postId = data.postId !== undefined && data.postId !== null ? String(data.postId) : ''
|
||||
}
|
||||
|
||||
const onStatusChange = (e) => {
|
||||
formData.status = statusOptions[e.detail.value].value
|
||||
}
|
||||
|
||||
const validateForm = async () => {
|
||||
if (!formRef.value || typeof formRef.value.validate !== 'function') {
|
||||
throw new Error('表单未准备就绪')
|
||||
}
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
return true
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const buildPayload = () => {
|
||||
const raw = toRaw(formData)
|
||||
const payload = {
|
||||
...raw,
|
||||
status: Number(raw.status)
|
||||
}
|
||||
if (Number.isNaN(payload.status)) {
|
||||
payload.status = 0
|
||||
}
|
||||
if (payload.amount !== undefined && payload.amount !== null && payload.amount !== '') {
|
||||
const amountNumber = Number(payload.amount)
|
||||
payload.amount = Number.isNaN(amountNumber) ? payload.amount : amountNumber
|
||||
}
|
||||
if (formType.value === 'create') {
|
||||
delete payload.id
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
if (submitting.value) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await validateForm()
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
|
||||
const payload = buildPayload()
|
||||
const apiFn = formType.value === 'create' ? DemoContractApi.createDemoContract : DemoContractApi.updateDemoContract
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
const result = await apiFn(payload)
|
||||
if (!result || result.code !== 0) {
|
||||
return
|
||||
}
|
||||
uni.showToast({
|
||||
title: formType.value === 'create' ? '新增成功' : '修改成功',
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 600)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-container {
|
||||
padding: 30rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.submit-btn {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
</style>
|
||||
192
pages/app/democontract/index.vue
Normal file
192
pages/app/democontract/index.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<view class="app-container">
|
||||
<!-- 搜索工作栏 -->
|
||||
<view class="search-section">
|
||||
<u-search
|
||||
placeholder="请输入合同名称"
|
||||
v-model="queryParams.name"
|
||||
:show-action="true"
|
||||
action-text="搜索"
|
||||
@custom="handleQuery"
|
||||
@search="handleQuery"
|
||||
></u-search>
|
||||
|
||||
<!-- 高级搜索 -->
|
||||
<view class="advanced-search" v-if="showAdvanced">
|
||||
<u-form :model="queryParams" label-width="120rpx">
|
||||
<u-form-item label="合同编号">
|
||||
<u-input v-model="queryParams.code" placeholder="请输入合同编号" />
|
||||
</u-form-item>
|
||||
<u-form-item label="合同金额">
|
||||
<u-input v-model="queryParams.amount" placeholder="请输入合同金额" type="number" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
|
||||
<view class="search-toggle">
|
||||
<u-button type="info" size="small" @click="showAdvanced = !showAdvanced">
|
||||
{{ showAdvanced ? '收起' : '高级搜索' }}
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<view class="action-bar">
|
||||
<u-button type="primary" icon="plus" text="新增" @click="openForm('create')"></u-button>
|
||||
</view>
|
||||
|
||||
<!-- 列表 -->
|
||||
<u-list @scrolltolower="scrolltolower">
|
||||
<u-list-item v-for="item in list" :key="item.id">
|
||||
<u-cell :title="item.name" :label="item.code">
|
||||
<template #value>
|
||||
<view class="item-info">
|
||||
<text class="info-text">状态: {{ getStatusText(item.status) }}</text>
|
||||
<text class="info-text">金额: ¥{{ item.amount || 0 }}</text>
|
||||
<text class="info-text" v-if="item.signDate">签订: {{ sheep.$helper.timeFormat(item.signDate, 'yyyy-mm-dd') }}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<view class="action-buttons">
|
||||
<u-button type="primary" size="mini" @click="openForm('update', item.id)">编辑</u-button>
|
||||
<u-button type="error" size="mini" @click="handleDelete(item.id)">删除</u-button>
|
||||
</view>
|
||||
</template>
|
||||
</u-cell>
|
||||
</u-list-item>
|
||||
<u-loadmore :status="status" />
|
||||
</u-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import DemoContractApi from '@/sheep/api/infra/democontract'
|
||||
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
|
||||
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const list = ref([])
|
||||
const showAdvanced = ref(false)
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
code: '',
|
||||
name: '',
|
||||
status: '',
|
||||
signDate: [],
|
||||
startDate: [],
|
||||
endDate: [],
|
||||
amount: '',
|
||||
remark: '',
|
||||
postId: ''
|
||||
})
|
||||
const status = ref('loadmore')
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
status.value = 'loading'
|
||||
try {
|
||||
const { data } = await DemoContractApi.getDemoContractPage(queryParams)
|
||||
if (queryParams.pageNo === 1) {
|
||||
list.value = data.list
|
||||
} else {
|
||||
list.value = list.value.concat(data.list)
|
||||
}
|
||||
total.value = data.total
|
||||
if (list.value.length >= total.value) {
|
||||
status.value = 'nomore'
|
||||
} else {
|
||||
status.value = 'loadmore'
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
uni.stopPullDownRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const openForm = (type, id) => {
|
||||
let url = `/pages/app/democontract/form?type=${type}`
|
||||
if (id) {
|
||||
url += `&id=${id}`
|
||||
}
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
const [err, res] = await uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除吗?'
|
||||
})
|
||||
if (res && res.confirm) {
|
||||
await DemoContractApi.deleteDemoContract(id)
|
||||
uni.showToast({ title: '删除成功' })
|
||||
handleQuery() // 重新加载列表
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
// 根据实际状态值进行映射,这里需要根据字典或枚举值调整
|
||||
const statusMap = {
|
||||
0: '草稿',
|
||||
1: '审核中',
|
||||
2: '已通过',
|
||||
3: '已拒绝'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
const scrolltolower = () => {
|
||||
if (status.value === 'loadmore') {
|
||||
queryParams.pageNo++
|
||||
getList()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
handleQuery()
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
scrolltolower()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-wrap {
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.action-bar {
|
||||
padding: 20rpx;
|
||||
}
|
||||
.list-wrap {
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
.item-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.item-desc {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
452
pages/app/sign.vue
Normal file
452
pages/app/sign.vue
Normal file
@@ -0,0 +1,452 @@
|
||||
<!-- 签到界面 -->
|
||||
<template>
|
||||
<s-layout title="签到有礼">
|
||||
<s-empty v-if="state.loading" icon="/static/data-empty.png" text="签到活动还未开始" />
|
||||
<view v-if="state.loading" />
|
||||
<view class="sign-wrap" v-else-if="!state.loading">
|
||||
<!-- 签到日历 -->
|
||||
<view class="content-box calendar">
|
||||
<view class="sign-everyday ss-flex ss-col-center ss-row-between ss-p-x-30">
|
||||
<text class="sign-everyday-title">签到日历</text>
|
||||
<view class="sign-num-box">
|
||||
已连续签到 <text class="sign-num">{{ state.signInfo.continuousDay }}</text> 天
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="list acea-row row-between-wrapper"
|
||||
style="
|
||||
padding: 0 30rpx;
|
||||
height: 240rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<view class="item" v-for="(item, index) in state.signConfigList" :key="index">
|
||||
<view
|
||||
:class="
|
||||
(index === state.signConfigList.length ? 'reward' : '') +
|
||||
' ' +
|
||||
(state.signInfo.continuousDay >= item.day ? 'rewardTxt' : '')
|
||||
"
|
||||
>
|
||||
第{{ item.day }}天
|
||||
</view>
|
||||
<view
|
||||
class="venus"
|
||||
:class="
|
||||
(index + 1 === state.signConfigList.length ? 'reward' : '') +
|
||||
' ' +
|
||||
(state.signInfo.continuousDay >= item.day ? 'venusSelect' : '')
|
||||
"
|
||||
>
|
||||
</view>
|
||||
<view class="num" :class="state.signInfo.continuousDay >= item.day ? 'on' : ''">
|
||||
+ {{ item.point }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 签到按钮 -->
|
||||
<view class="myDateTable">
|
||||
<view class="ss-flex ss-col-center ss-row-center sign-box ss-m-y-40">
|
||||
<button
|
||||
class="ss-reset-button sign-btn"
|
||||
v-if="!state.signInfo.todaySignIn"
|
||||
@tap="onSign"
|
||||
>
|
||||
签到
|
||||
</button>
|
||||
<button class="ss-reset-button already-btn" v-else disabled> 已签到 </button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 签到说明 -->
|
||||
<view class="bg-white ss-m-t-16 ss-p-t-30 ss-p-b-60 ss-p-x-40">
|
||||
<view class="activity-title ss-m-b-30">签到说明</view>
|
||||
<view class="activity-des">1.已累计签到{{ state.signInfo.totalDay }}天</view>
|
||||
<view class="activity-des">
|
||||
2.据说连续签到第 {{ state.maxDay }} 天可获得超额积分,要坚持签到哦~~
|
||||
</view>
|
||||
<view class="activity-des"> 3.积分可以在购物时抵现金结算的哦 ~~</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 签到结果弹窗 -->
|
||||
<su-popup :show="state.showModel" type="center" round="10" :isMaskClick="false">
|
||||
<view class="model-box ss-flex-col">
|
||||
<view class="ss-m-t-56 ss-flex-col ss-col-center">
|
||||
<text class="cicon-check-round"></text>
|
||||
<view class="score-title">
|
||||
<text v-if="state.signResult.point">{{ state.signResult.point }} 积分 </text>
|
||||
<text v-if="state.signResult.experience"> {{ state.signResult.experience }} 经验</text>
|
||||
</view>
|
||||
<view class="model-title ss-flex ss-col-center ss-m-t-22 ss-m-b-30">
|
||||
已连续打卡 {{ state.signResult.day }} 天
|
||||
</view>
|
||||
</view>
|
||||
<view class="model-bg ss-flex-col ss-col-center ss-row-right">
|
||||
<view class="title ss-m-b-64">签到成功</view>
|
||||
<view class="ss-m-b-40">
|
||||
<button class="ss-reset-button confirm-btn" @tap="onConfirm">确认</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</su-popup>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onReady } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
import SignInApi from '@/sheep/api/member/signin';
|
||||
|
||||
const headerBg = sheep.$url.css('/static/img/shop/app/sign.png');
|
||||
|
||||
const state = reactive({
|
||||
loading: true,
|
||||
|
||||
signInfo: {}, // 签到信息
|
||||
|
||||
signConfigList: [], // 签到配置列表
|
||||
maxDay: 0, // 最大的签到天数
|
||||
|
||||
showModel: false, // 签到弹框
|
||||
signResult: {}, // 签到结果
|
||||
});
|
||||
|
||||
// 发起签到
|
||||
async function onSign() {
|
||||
const { code, data } = await SignInApi.createSignInRecord();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.showModel = true;
|
||||
state.signResult = data;
|
||||
// 重新获得签到信息
|
||||
await getSignInfo();
|
||||
}
|
||||
|
||||
// 签到确认刷新页面
|
||||
function onConfirm() {
|
||||
state.showModel = false;
|
||||
}
|
||||
|
||||
// 获得个人签到统计
|
||||
async function getSignInfo() {
|
||||
const { code, data } = await SignInApi.getSignInRecordSummary();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.signInfo = data;
|
||||
state.loading = false;
|
||||
}
|
||||
|
||||
// 获取签到配置
|
||||
async function getSignConfigList() {
|
||||
const { code, data } = await SignInApi.getSignInConfigList();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
state.signConfigList = data;
|
||||
if (data.length > 0) {
|
||||
state.maxDay = data[data.length - 1].day;
|
||||
}
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
getSignInfo();
|
||||
getSignConfigList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-box {
|
||||
border-top: 2rpx solid rgba(#dfdfdf, 0.5);
|
||||
}
|
||||
|
||||
// 日历
|
||||
.calendar {
|
||||
background: #fff;
|
||||
|
||||
.sign-everyday {
|
||||
height: 100rpx;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border: 2rpx solid rgba(223, 223, 223, 0.4);
|
||||
|
||||
.sign-everyday-title {
|
||||
font-size: 32rpx;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sign-num-box {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
|
||||
.sign-num {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #ff6000;
|
||||
padding: 0 10rpx;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 年月日
|
||||
.bar {
|
||||
height: 100rpx;
|
||||
|
||||
.date {
|
||||
font-size: 30rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.cicon-back {
|
||||
margin-top: 6rpx;
|
||||
font-size: 30rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-forward {
|
||||
margin-top: 6rpx;
|
||||
font-size: 30rpx;
|
||||
color: #c4c4c4;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
// 星期
|
||||
.week {
|
||||
.week-item {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(153, 153, 153, 1);
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 日历表
|
||||
.myDateTable {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.dateCell {
|
||||
width: calc(750rpx / 7);
|
||||
height: 80rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-sign {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
position: relative;
|
||||
|
||||
.is-sign-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.is-sign-image {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cell-num {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.cicon-title {
|
||||
position: absolute;
|
||||
right: -10rpx;
|
||||
top: -6rpx;
|
||||
font-size: 20rpx;
|
||||
color: red;
|
||||
}
|
||||
|
||||
// 签到按钮
|
||||
.sign-box {
|
||||
height: 140rpx;
|
||||
width: 100%;
|
||||
|
||||
.sign-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 0.2em 0.5em rgba(#ff6000, 0.4);
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.already-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 35rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.model-box {
|
||||
width: 520rpx;
|
||||
// height: 590rpx;
|
||||
background: linear-gradient(177deg, #ff6000 0%, #fe832a 100%);
|
||||
// background: linear-gradient(177deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||||
border-radius: 10rpx;
|
||||
|
||||
.cicon-check-round {
|
||||
font-size: 70rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.score-title {
|
||||
font-size: 34rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #fcff00;
|
||||
}
|
||||
|
||||
.model-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.model-bg {
|
||||
width: 520rpx;
|
||||
height: 344rpx;
|
||||
background-size: 100% 100%;
|
||||
background-image: v-bind(headerBg);
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
|
||||
.title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: var(--ui-BG-Main-TC);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 220rpx;
|
||||
height: 70rpx;
|
||||
border: 2rpx solid #ff6000;
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ff6000;
|
||||
line-height: normal;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 220rpx;
|
||||
height: 70rpx;
|
||||
background: linear-gradient(90deg, #ff6000, #fe832a);
|
||||
box-shadow: 0 0.2em 0.5em rgba(#ff6000, 0.4);
|
||||
border-radius: 35rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//签到说明
|
||||
.activity-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.activity-des {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.reward {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAAA4CAYAAAC1+AWFAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUEyNzZEMjZEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUEyNzZEMjVEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIv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');
|
||||
width: 75rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.rewardTxt {
|
||||
width: 74rpx;
|
||||
height: 32rpx;
|
||||
background-color: #f4b409;
|
||||
border-radius: 16rpx;
|
||||
font-size: 20rpx;
|
||||
color: var(--ui-BG-Main-TC);
|
||||
line-height: 32rpx;
|
||||
text-align: center;
|
||||
padding: 2rpx;
|
||||
}
|
||||
|
||||
.venus {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAYAAACohjseAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RDQ3N0E3RkJEMDFCMTFFODhGODVBOTc1NEM5Nzg2NzkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RDQ3N0E3RkFEMDFCMTFFODhGODVBOTc1NEM5Nzg2NzkiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnZClGIAAAT7SURBVHja7JtLcxtFEMdnn1rJUhIrTiS7KLsK/EXwNck3gCMcuUFx4QvACW48buHGMZxJPkiowpjC2LElcKzXSqul/6OdoOB9zezsCCimqmvXtdqd+U33dPf0rq2Tn54zg81KjrGuB75x8FbuddsQWIvkS5IxySA5b5no2DUE94TkKPk7IHmf5JDkUQJdW7MNw623o+Ra698KmAdnDNLeIJwRSHvDcLVDVnYynU771fnLl9eFcLZts+VymQf5iJ6pzfFYJeKgT/IZyTskXdWOPM9jjYbPLMticRyz2Sxk8/lc9XFDksckH1IcDKtq8FOSD6rMIuCCoPHXrBIk/qYDC0MlyO1kTBOSj6uuwXerwPn+63DrrdFo8OsV2ns6nEy3Chwg8lpFyK4OwNrgNGpSvxfFoDzP5etJR8PzsiYETmk+X5BjmpkBrHPGU109TeKqv5X3rT3QQ3ObaPDGRjIZXWZpol+b/cebUUA4iuHw938UoNbk9+zsjP16eqoV4JfjH1uqgLjxe10DiaKIDYZDNqfU7OrqSifjkzxIuwDubV2juLi8ZK7j8oT74uJSJ+BRHqQtC6cS/7A9wtrDvb7v84A9Go2UMyQZSFsWrmz6td4GgyF38a7r8lgGefHiQjnjkYG064ZDmjWktecRnEOCDa9DpjqdTrnUDSk2vJXgsHGdTKa0t5vx/V0Yhvy4WMx5hdf3VqYJDQqTxW+QdmECPM8h8flvPPRFR/QXBEEuKEw9Yz/5AyoDtBkei5zriyw4pGVFmoOHPD7+mQ/ccR1mWzZpyuIacx2Hgzl0FJkIjjBTHBeLiO6LuDZH4zFbRkt63oLt7+8XAmJcIhFP0eTn2C9CgzBTrPjUp7XbW6VSJGjx5OSEBhsz2wGkxc0R967LuukuSWKaFEwMnXJQSL/fZ51Op/QSuL5OdVjY7beFBq2sAlHZ/A8aOTg4YKenv/EBr8Nl5ZUO6jPJRGCg0dJie/d3WbPZlMpPMwpZsN0Y2sOVb7PcOzou22CGe3u7fB3J3Cd+udvvScG9soT0Kt13tAZj4UVRwHmWtZClcj+azX6vxx0GN8ECUFyHjnsEp+KlM8b3TBTKBCDqkA/SIFV20jCbO3duk8OIcgHF7G9ttbinVYFLcTBgeAAP+vc4mAkJVywLibARpZvOjWWA36rApYSI1+DSMhltkJPpRGIyJrXAZeWiWiCnk9kNU4RGl8m5MN1VLFzwWKobLm83kQtZbnMb8lgYJ2aIYH//3g73lEHQ5ECAXYUSu7QWZeCKqmoCcqSyPkQgd8lHbne3uSMRbWenSwNtU476BxtT9oJQOZ3OKKloq6SmmXBlyoZKb3nG4wnXyt1ul9261UkN9ggjvd497mDOz885KGN3pfvKg1OuixY15JCHh2/ymFjUms2AMqD9BFB/qwUQg5ZtrVY9b7H/LxtmZSCbaCr9KgGmpEeG6qpzM2tQBHudb5eKNLfKiUMzgAIyL6uRreHklB9qX4MDlQfLpHUV4AY6AB+rzl4ZyIqa+0aHiX6UlDTwMcK2CqQoEGmEE5+RfFK4N636vWjKh0Cp5ceS38k8JXko8yHQ7W7PXKBPBvYwGegNF4/q12g05mV7HXClqgs1ffEr+/LmaTIx0nCb+uI3U5M64Tadi5aBrBXORLKdB1k7nKndhID8GqUaErze/coEXJ1OJm9CY2bw3wr+FGAAoa6PIUihovYAAAAASUVORK5CYII=');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
margin: 10rpx auto;
|
||||
}
|
||||
|
||||
.venusSelect {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAYAAACohjseAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzkwRkI4NEFEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzkwRkI4NDlEMDFCMTFFODhDNDdBMDVGOTBBN0U2NTQiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIv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');
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 36rpx;
|
||||
font-family: 'Guildford Pro';
|
||||
}
|
||||
|
||||
.item {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
height: 130rpx;
|
||||
}
|
||||
|
||||
.reward {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAAA4CAYAAAC1+AWFAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowYWFmYjU3Mi03MGJhLTRiNDctOTI2Yi0zOThlZDkzZDkxMDkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUEyNzZEMjZEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUEyNzZEMjVEMDFCMTFFOEIzQzhEMjMxNjI1NENDQjciIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZmRjNTM0MmUtNmFkOC1iMDRhLThjZTEtMjk2YWYzM2FkMmUxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjBhYWZiNTcyLTcwYmEtNGI0Ny05MjZiLTM5OGVkOTNkOTEwOSIv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');
|
||||
width: 75rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.on {
|
||||
color: #f4b409;
|
||||
}
|
||||
</style>
|
||||
81
pages/components/HighlightNumberText.vue
Normal file
81
pages/components/HighlightNumberText.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<view class="content-container">
|
||||
<span v-for="(part, index) in formattedContent" :key="index"
|
||||
@click="handleClick(part)"
|
||||
:class="{'highlight-number': part.isNumber, 'phone-number': part.isPhone}">
|
||||
{{ part.text }}
|
||||
</span>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HighlightNumber',
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedContent() {
|
||||
const phoneRegex = /(1[3-9]\d{9})/g;
|
||||
const numberRegex = /(\d+)/g;
|
||||
let text = this.content;
|
||||
let result = [];
|
||||
let match;
|
||||
|
||||
// Step 1: 提取手机号
|
||||
while ((match = phoneRegex.exec(text)) !== null) {
|
||||
if (match.index > 0) {
|
||||
const before = text.slice(0, match.index);
|
||||
result.push(...this.splitAndPush(before, false, false));
|
||||
}
|
||||
result.push({ text: match[0], isNumber: true, isPhone: true });
|
||||
text = text.slice(match.index + match[0].length);
|
||||
}
|
||||
|
||||
// Step 2: 提取普通数字
|
||||
while ((match = numberRegex.exec(text)) !== null) {
|
||||
if (match.index > 0) {
|
||||
const before = text.slice(0, match.index);
|
||||
result.push(...this.splitAndPush(before, false, false));
|
||||
}
|
||||
result.push({ text: match[0], isNumber: true });
|
||||
text = text.slice(match.index + match[0].length);
|
||||
}
|
||||
|
||||
// Step 3: 添加剩余文本
|
||||
if (text.length > 0) {
|
||||
result.push(...this.splitAndPush(text, false, false));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
splitAndPush(str, isNumber = false, isPhone = false) {
|
||||
return str.split('').map(char => ({ text: char, isNumber, isPhone }));
|
||||
},
|
||||
handleClick(part) {
|
||||
if (part.isPhone) {
|
||||
this.$emit('phone-click', { phoneNumber: part.text });
|
||||
} else if (part.isNumber) {
|
||||
this.$emit('number-click', { number: part.text });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.highlight-number {
|
||||
color: #ff5722;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.phone-number {
|
||||
color: #007AFF;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
227
pages/index/category.vue
Normal file
227
pages/index/category.vue
Normal file
@@ -0,0 +1,227 @@
|
||||
<!-- 商品分类列表 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#fff' }" tabbar="/pages/index/category" title="分类">
|
||||
<view class="s-category">
|
||||
<view class="three-level-wrap ss-flex ss-col-top">
|
||||
<!-- 商品分类(左) -->
|
||||
<view class="side-menu-wrap" :style="[{ top: Number(statusBarHeight + 88) + 'rpx' }]">
|
||||
<scroll-view scroll-y :style="[{ height: pageHeight + 'px' }]">
|
||||
<view
|
||||
class="menu-item ss-flex"
|
||||
v-for="(item, index) in state.categoryList"
|
||||
:key="item.id"
|
||||
:class="[{ 'menu-item-active': index === state.activeMenu }]"
|
||||
@tap="onMenu(index)"
|
||||
>
|
||||
<view class="menu-title ss-line-1">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 商品分类(右) -->
|
||||
<view class="goods-list-box" v-if="state.categoryList?.length">
|
||||
<scroll-view scroll-y :style="[{ height: pageHeight + 'px' }]">
|
||||
<image
|
||||
v-if="state.categoryList[state.activeMenu].picUrl"
|
||||
class="banner-img"
|
||||
:src="sheep.$url.cdn(state.categoryList[state.activeMenu].picUrl)"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<first-one v-if="state.style === 'first_one'" :pagination="state.pagination" />
|
||||
<first-two v-if="state.style === 'first_two'" :pagination="state.pagination" />
|
||||
<second-one
|
||||
v-if="state.style === 'second_one'"
|
||||
:data="state.categoryList"
|
||||
:activeMenu="state.activeMenu"
|
||||
/>
|
||||
<uni-load-more
|
||||
v-if="
|
||||
(state.style === 'first_one' || state.style === 'first_two') &&
|
||||
state.pagination.total > 0
|
||||
"
|
||||
:status="state.loadStatus"
|
||||
:content-text="{
|
||||
contentdown: '点击查看更多',
|
||||
}"
|
||||
@tap="loadMore"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import secondOne from './components/second-one.vue';
|
||||
import firstOne from './components/first-one.vue';
|
||||
import firstTwo from './components/first-two.vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { computed, reactive } from 'vue';
|
||||
import _ from 'lodash-es';
|
||||
import { handleTree } from '@/sheep/helper/utils';
|
||||
|
||||
const state = reactive({
|
||||
style: 'second_one', // first_one(一级 - 样式一), first_two(二级 - 样式二), second_one(二级)
|
||||
categoryList: [], // 商品分类树
|
||||
activeMenu: 0, // 选中的一级菜单,在 categoryList 的下标
|
||||
|
||||
pagination: {
|
||||
// 商品分页
|
||||
list: [], // 商品列表
|
||||
total: [], // 商品总数
|
||||
pageNo: 1,
|
||||
pageSize: 6,
|
||||
},
|
||||
loadStatus: '',
|
||||
});
|
||||
|
||||
const { safeArea } = sheep.$platform.device;
|
||||
const pageHeight = computed(() => safeArea.height - 44 - 50);
|
||||
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
|
||||
|
||||
// 加载商品分类
|
||||
async function getList() {
|
||||
// API已被移除,返回空数据
|
||||
state.categoryList = [];
|
||||
}
|
||||
|
||||
// 选中菜单
|
||||
const onMenu = (val) => {
|
||||
state.activeMenu = val;
|
||||
if (state.style === 'first_one' || state.style === 'first_two') {
|
||||
state.pagination.pageNo = 1;
|
||||
state.pagination.list = [];
|
||||
state.pagination.total = 0;
|
||||
getGoodsList();
|
||||
}
|
||||
};
|
||||
|
||||
// 加载商品列表
|
||||
async function getGoodsList() {
|
||||
// 加载列表
|
||||
state.loadStatus = 'loading';
|
||||
// API已被移除,返回空数据
|
||||
state.pagination.list = [];
|
||||
state.pagination.total = 0;
|
||||
state.loadStatus = 'noMore';
|
||||
}
|
||||
|
||||
// 加载更多商品
|
||||
function loadMore() {
|
||||
if (state.loadStatus === 'noMore') {
|
||||
return;
|
||||
}
|
||||
state.pagination.pageNo++;
|
||||
getGoodsList();
|
||||
}
|
||||
|
||||
onLoad(async (params) => {
|
||||
await getList();
|
||||
|
||||
// 首页点击分类的处理:查找满足条件的分类
|
||||
const foundCategory = state.categoryList.find((category) => category.id === Number(params.id));
|
||||
// 如果找到则调用 onMenu 自动勾选相应分类,否则调用 onMenu(0) 勾选第一个分类
|
||||
onMenu(foundCategory ? state.categoryList.indexOf(foundCategory) : 0);
|
||||
});
|
||||
|
||||
function handleScrollToLower() {
|
||||
loadMore();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.s-category {
|
||||
:deep() {
|
||||
.side-menu-wrap {
|
||||
width: 200rpx;
|
||||
height: 100%;
|
||||
padding-left: 12rpx;
|
||||
background-color: #f6f6f6;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
.menu-item {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
position: relative;
|
||||
transition: all linear 0.2s;
|
||||
|
||||
.menu-title {
|
||||
line-height: 32rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
margin-left: 28rpx;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 64rpx;
|
||||
height: 12rpx;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--ui-BG-Main-gradient),
|
||||
var(--ui-BG-Main-light)
|
||||
) !important;
|
||||
position: absolute;
|
||||
left: -64rpx;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
&.menu-item-active {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 0 0 20rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -20rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 100%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -20rpx;
|
||||
right: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0% 0%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-weight: 600;
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-list-box {
|
||||
background-color: #fff;
|
||||
width: calc(100vw - 200rpx);
|
||||
padding: 10px;
|
||||
margin-left: 200rpx;
|
||||
}
|
||||
|
||||
.banner-img {
|
||||
width: calc(100vw - 130px);
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
23
pages/index/components/first-one.vue
Normal file
23
pages/index/components/first-one.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<!-- 分类展示:first-one 风格 -->
|
||||
<template>
|
||||
<view class="ss-flex-col">
|
||||
<!-- 商品组件已被移除 -->
|
||||
<view class="empty-message" style="text-align: center; padding: 50px; color: #999;">
|
||||
暂无商品数据
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
66
pages/index/components/first-two.vue
Normal file
66
pages/index/components/first-two.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<!-- 分类展示:first-two 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<view class="ss-flex flex-wrap">
|
||||
<view class="goods-box" v-for="item in pagination?.list" :key="item.id">
|
||||
<view @click="sheep.$router.go('/pages/goods/index', { id: item.id })">
|
||||
<view class="goods-img">
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="goods-content">
|
||||
<view class="goods-title ss-line-1 ss-m-b-28">{{ item.name }}</view>
|
||||
<view class="goods-price">¥{{ fen2yuan(item.price) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { fen2yuan } from '@/sheep/hooks/useGoods';
|
||||
|
||||
const props = defineProps({
|
||||
pagination: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.goods-box {
|
||||
width: calc((100% - 20rpx) / 2);
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.goods-img {
|
||||
width: 100%;
|
||||
height: 246rpx;
|
||||
border-radius: 10rpx 10rpx 0px 0px;
|
||||
}
|
||||
|
||||
.goods-content {
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0px 0px 20rpx 4rpx rgba(199, 199, 199, 0.22);
|
||||
padding: 20rpx 0 32rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 0 10rpx 10rpx;
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
font-size: 24rpx;
|
||||
font-family: OPPOSANS;
|
||||
font-weight: 500;
|
||||
color: #e1212b;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n + 1) {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
pages/index/components/second-one.vue
Normal file
80
pages/index/components/second-one.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<!-- 分类展示:second-one 风格 -->
|
||||
<template>
|
||||
<view>
|
||||
<!-- 一级分类的名字 -->
|
||||
<view class="title-box ss-flex ss-col-center ss-row-center ss-p-b-30">
|
||||
<view class="title-line-left" />
|
||||
<view class="title-text ss-p-x-20">{{ props.data[activeMenu].name }}</view>
|
||||
<view class="title-line-right" />
|
||||
</view>
|
||||
<!-- 二级分类的名字 -->
|
||||
<view class="goods-item-box ss-flex ss-flex-wrap ss-p-b-20">
|
||||
<view
|
||||
class="goods-item"
|
||||
v-for="item in props.data[activeMenu].children"
|
||||
:key="item.id"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/goods/list', {
|
||||
categoryId: item.id,
|
||||
})
|
||||
"
|
||||
>
|
||||
<image class="goods-img" :src="item.picUrl" mode="aspectFill" />
|
||||
<view class="ss-p-10">
|
||||
<view class="goods-title ss-line-1">{{ item.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
activeMenu: [Number, String],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title-box {
|
||||
.title-line-left,
|
||||
.title-line-right {
|
||||
width: 15px;
|
||||
height: 1px;
|
||||
background: #d2d2d2;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
width: calc((100% - 20px) / 3);
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:nth-of-type(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
width: calc((100vw - 140px) / 3);
|
||||
height: calc((100vw - 140px) / 3);
|
||||
}
|
||||
|
||||
.goods-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
line-height: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
color: $red;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
547
pages/index/index.vue
Normal file
547
pages/index/index.vue
Normal file
@@ -0,0 +1,547 @@
|
||||
<!-- 首页 - 重新设计 -->
|
||||
<template>
|
||||
<view class="container">
|
||||
<s-layout
|
||||
title="首页"
|
||||
navbar="custom"
|
||||
tabbar="/pages/index/index"
|
||||
onShareAppMessage
|
||||
>
|
||||
<!-- 用户信息区域 - 使用 uview-plus Card 组件 -->
|
||||
<u-card
|
||||
v-if="isLogin"
|
||||
:show-head="false"
|
||||
:show-foot="false"
|
||||
margin="30rpx"
|
||||
>
|
||||
<template #body>
|
||||
<view class="user-info-section">
|
||||
<view class="user-avatar">
|
||||
<s-avatar
|
||||
:src="userInfo.avatar"
|
||||
:nickname="userInfo.nickname || userInfo.username"
|
||||
:size="60"
|
||||
:bg-color="getAvatarBgColor(userInfo.nickname || userInfo.username)"
|
||||
@click="handleUserClick"
|
||||
/>
|
||||
</view>
|
||||
<view class="user-details">
|
||||
<u-text
|
||||
:text="userInfo.nickname || '用户'"
|
||||
size="18"
|
||||
color="#333"
|
||||
bold
|
||||
/>
|
||||
<u-text
|
||||
:text="userInfo.mobile || '欢迎使用系统'"
|
||||
size="14"
|
||||
color="#999"
|
||||
margin="6rpx 0 0 0"
|
||||
/>
|
||||
</view>
|
||||
<view class="user-actions">
|
||||
<u-button
|
||||
text="个人中心"
|
||||
size="mini"
|
||||
type="primary"
|
||||
plain
|
||||
@click="navigateTo('/pages/index/user')"
|
||||
margin="0 0 10rpx 0"
|
||||
/>
|
||||
<u-button
|
||||
text="退出登录"
|
||||
size="mini"
|
||||
type="error"
|
||||
plain
|
||||
@click="handleLogout"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<!-- 未登录提示区域 - 使用 uview-plus Card 和 Button -->
|
||||
<u-card
|
||||
v-else
|
||||
:show-head="false"
|
||||
:show-foot="false"
|
||||
margin="30rpx"
|
||||
>
|
||||
<template #body>
|
||||
<view class="login-prompt">
|
||||
<view class="prompt-content">
|
||||
<u-text text="欢迎使用" size="20" color="#333" bold />
|
||||
<u-text
|
||||
text="请先登录以使用完整功能"
|
||||
size="14"
|
||||
color="#999"
|
||||
margin="10rpx 0 30rpx 0"
|
||||
/>
|
||||
<u-button
|
||||
text="立即登录"
|
||||
type="primary"
|
||||
size="normal"
|
||||
@click="goToLogin"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<!-- 功能模块入口 - 使用 uview-plus Grid 组件 -->
|
||||
<u-card
|
||||
v-if="isLogin"
|
||||
:show-head="false"
|
||||
:show-foot="false"
|
||||
margin="30rpx"
|
||||
>
|
||||
<template #body>
|
||||
<view class="function-modules">
|
||||
<view class="section-title">
|
||||
<u-text text="功能模块" size="16" color="#333" bold />
|
||||
</view>
|
||||
<u-grid :col="3" :border="false">
|
||||
<u-grid-item
|
||||
v-for="module in functionModules"
|
||||
:key="module.id"
|
||||
@click="handleModuleClick(module)"
|
||||
>
|
||||
<view class="module-content">
|
||||
<view class="module-icon" :style="{ backgroundColor: module.color + '20' }">
|
||||
<u-icon :name="module.icon" size="24" :color="module.color" />
|
||||
</view>
|
||||
<u-text
|
||||
:text="module.name"
|
||||
size="12"
|
||||
color="#666"
|
||||
margin="10rpx 0 0 0"
|
||||
/>
|
||||
</view>
|
||||
</u-grid-item>
|
||||
</u-grid>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
</s-layout>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad, onPullDownRefresh, onShow } from '@dcloudio/uni-app';
|
||||
import { computed, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { getAvatarBgColor } from '@/sheep/utils/avatar.js';
|
||||
|
||||
// 隐藏原生tabBar
|
||||
uni.hideTabBar({
|
||||
fail: () => {},
|
||||
});
|
||||
|
||||
// 跳转到登录页
|
||||
function goToLogin() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
}
|
||||
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
// 用于防止重复验证
|
||||
let isValidating = false;
|
||||
|
||||
// 用户头像点击事件
|
||||
function handleUserClick() {
|
||||
if (!isLogin.value) {
|
||||
goToLogin();
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/user'
|
||||
});
|
||||
}
|
||||
|
||||
// 格式化最后登录时间
|
||||
const formatLastLoginTime = computed(() => {
|
||||
const now = new Date();
|
||||
return `${now.getMonth() + 1}月${now.getDate()}日 ${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||||
});
|
||||
|
||||
// 功能模块数据
|
||||
const functionModules = ref([
|
||||
{
|
||||
id: 'profile',
|
||||
name: '个人资料',
|
||||
icon: 'account',
|
||||
color: 'var(--ui-BG-Main)', // 主题色
|
||||
path: '/pages/index/user'
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
name: '系统设置',
|
||||
icon: 'setting',
|
||||
color: '#666666', // $dark-6
|
||||
action: 'showSettings'
|
||||
},
|
||||
{
|
||||
id: 'security',
|
||||
name: '安全中心',
|
||||
icon: 'lock',
|
||||
color: '#d10019', // $red
|
||||
action: 'showSecurity'
|
||||
},
|
||||
{
|
||||
id: 'feedback',
|
||||
name: '意见反馈',
|
||||
icon: 'chat',
|
||||
color: '#8dc63f', // $green
|
||||
action: 'showFeedback'
|
||||
}
|
||||
]);
|
||||
|
||||
// 快捷操作数据
|
||||
const quickActions = ref([
|
||||
|
||||
{
|
||||
id: 'logout',
|
||||
title: '退出登录',
|
||||
desc: '安全退出当前账户',
|
||||
icon: 'logout',
|
||||
action: 'logout'
|
||||
}
|
||||
]);
|
||||
|
||||
onLoad((options) => {
|
||||
console.log('首页加载完成');
|
||||
checkLoginStatus();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
// 只有在页面从后台返回前台时才需要重新验证登录状态
|
||||
// 避免首次加载时的重复验证
|
||||
console.log('页面显示,检查是否需要验证登录状态');
|
||||
|
||||
// 延迟一点时间,确保不与onLoad的验证冲突
|
||||
setTimeout(() => {
|
||||
checkLoginStatus();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
onPullDownRefresh(async () => {
|
||||
console.log('下拉刷新');
|
||||
try {
|
||||
// 刷新用户信息
|
||||
if (isLogin.value) {
|
||||
await sheep.$store('user').getInfo();
|
||||
console.log('用户信息刷新成功');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('刷新用户信息失败,可能登录已过期', error);
|
||||
// 如果刷新失败,可能是登录过期,重新验证登录状态
|
||||
await checkLoginStatus();
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查登录状态
|
||||
async function checkLoginStatus() {
|
||||
// 防止重复验证
|
||||
if (isValidating) {
|
||||
console.log('正在验证中,跳过重复验证');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('检查登录状态...');
|
||||
|
||||
// 如果本地显示未登录,直接跳转到登录页
|
||||
if (!isLogin.value) {
|
||||
console.log('本地状态未登录,跳转到登录页');
|
||||
setTimeout(() => {
|
||||
goToLogin();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果本地显示已登录,需要验证token是否还有效
|
||||
console.log('本地状态已登录,验证token有效性...');
|
||||
isValidating = true;
|
||||
|
||||
try {
|
||||
// 尝试获取用户信息来验证token是否有效
|
||||
await sheep.$store('user').getInfo();
|
||||
console.log('Token有效,用户已登录');
|
||||
} catch (error) {
|
||||
console.log('Token已失效,需要重新登录', error);
|
||||
// 清除本地登录状态
|
||||
sheep.$store('user').logout(false);
|
||||
// 延迟显示登录弹窗,确保页面渲染完成
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
title: '登录已过期,请重新登录',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
setTimeout(() => {
|
||||
goToLogin();
|
||||
}, 2000);
|
||||
}, 500);
|
||||
} finally {
|
||||
// 验证完成,重置标志
|
||||
setTimeout(() => {
|
||||
isValidating = false;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 导航到指定页面
|
||||
function navigateTo(path) {
|
||||
uni.navigateTo({
|
||||
url: path
|
||||
});
|
||||
}
|
||||
|
||||
// 处理退出登录
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要退出登录吗?',
|
||||
showCancel: true,
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
title: '退出中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
// 调用退出登录API
|
||||
await sheep.$store('user').logout(true);
|
||||
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading();
|
||||
|
||||
// 显示退出成功提示
|
||||
uni.showToast({
|
||||
title: '退出成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
|
||||
// 延迟跳转到登录页
|
||||
setTimeout(() => {
|
||||
goToLogin();
|
||||
}, 1500);
|
||||
|
||||
} catch (error) {
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading();
|
||||
|
||||
console.error('退出登录失败:', error);
|
||||
uni.showToast({
|
||||
title: '退出失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理模块点击
|
||||
function handleModuleClick(module) {
|
||||
if (module.path) {
|
||||
navigateTo(module.path);
|
||||
} else if (module.action) {
|
||||
handleAction(module.action);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理快捷操作点击
|
||||
function handleActionClick(action) {
|
||||
handleAction(action.action);
|
||||
}
|
||||
|
||||
// 处理各种操作
|
||||
function handleAction(action) {
|
||||
switch (action) {
|
||||
case 'showSettings':
|
||||
uni.showToast({
|
||||
title: '系统设置功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
break;
|
||||
case 'showSecurity':
|
||||
uni.showToast({
|
||||
title: '安全中心功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
break;
|
||||
case 'showFeedback':
|
||||
uni.showToast({
|
||||
title: '意见反馈功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
break;
|
||||
case 'showAbout':
|
||||
uni.showModal({
|
||||
title: '关于应用',
|
||||
content: '应用版本:v1.0.0\n开发者:Yudao Team\n更新时间:2024年',
|
||||
showCancel: false
|
||||
});
|
||||
break;
|
||||
case 'scanCode':
|
||||
uni.scanCode({
|
||||
success: (res) => {
|
||||
uni.showModal({
|
||||
title: '扫描结果',
|
||||
content: res.result,
|
||||
showCancel: false
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: '扫描失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'shareApp':
|
||||
uni.share({
|
||||
provider: 'weixin',
|
||||
scene: 'WXSceneSession',
|
||||
type: 0,
|
||||
href: '',
|
||||
title: '推荐一个好用的应用',
|
||||
summary: '快来试试这个应用吧!',
|
||||
imageUrl: ''
|
||||
});
|
||||
break;
|
||||
case 'clearCache':
|
||||
uni.showModal({
|
||||
title: '清理缓存',
|
||||
content: '确定要清理应用缓存吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 这里可以添加清理缓存的逻辑
|
||||
uni.clearStorageSync();
|
||||
uni.showToast({
|
||||
title: '缓存清理完成',
|
||||
icon: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'logout':
|
||||
handleLogout();
|
||||
break;
|
||||
default:
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 用户信息区域 */
|
||||
.user-info-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
/* 未登录提示区域 */
|
||||
.login-prompt {
|
||||
text-align: center;
|
||||
padding: 40rpx 20rpx;
|
||||
}
|
||||
|
||||
.prompt-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
/* 功能模块 */
|
||||
.function-modules {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.module-content {
|
||||
text-align: center;
|
||||
padding: 20rpx 10rpx;
|
||||
}
|
||||
|
||||
.module-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 10rpx;
|
||||
}
|
||||
|
||||
/* 快捷操作 */
|
||||
.quick-actions {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 15rpx;
|
||||
background: #f0f0f0;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
/* 系统信息 */
|
||||
.system-info {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15rpx 0;
|
||||
}
|
||||
</style>
|
||||
39
pages/index/login.vue
Normal file
39
pages/index/login.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<!-- 微信公众号的登录回调页 -->
|
||||
<template>
|
||||
<!-- 空登陆页 -->
|
||||
<view />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
onLoad(async (options) => {
|
||||
// #ifdef H5
|
||||
// 将 search 参数赋值到 options 中,方便下面解析
|
||||
new URLSearchParams(location.search).forEach((value, key) => {
|
||||
options[key] = value;
|
||||
});
|
||||
// 执行登录 or 绑定,注意需要 await 绑定
|
||||
const event = options.event;
|
||||
const code = options.code;
|
||||
const state = options.state;
|
||||
if (event === 'login') { // 场景一:登录
|
||||
await sheep.$platform.useProvider().login(code, state);
|
||||
} else if (event === 'bind') { // 场景二:绑定
|
||||
await sheep.$platform.useProvider().bind(code, state);
|
||||
}
|
||||
|
||||
// 检测 H5 登录回调
|
||||
let returnUrl = uni.getStorageSync('returnUrl');
|
||||
if (returnUrl) {
|
||||
uni.removeStorage({key:'returnUrl'});
|
||||
location.replace(returnUrl);
|
||||
} else {
|
||||
uni.switchTab({
|
||||
url: '/',
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
});
|
||||
</script>
|
||||
266
pages/index/menu.vue
Normal file
266
pages/index/menu.vue
Normal file
@@ -0,0 +1,266 @@
|
||||
<!-- 菜单页面 -->
|
||||
<template>
|
||||
<s-layout
|
||||
title="菜单"
|
||||
tabbar="/pages/index/menu"
|
||||
navbar="custom"
|
||||
onShareAppMessage
|
||||
>
|
||||
<view class="menu-container">
|
||||
<!-- 轮播图 -->
|
||||
<view class="swiper-section">
|
||||
<swiper
|
||||
class="swiper-container"
|
||||
:indicator-dots="true"
|
||||
:autoplay="true"
|
||||
:interval="3000"
|
||||
:duration="500"
|
||||
indicator-color="rgba(255, 255, 255, 0.3)"
|
||||
indicator-active-color="#fff"
|
||||
circular
|
||||
>
|
||||
<swiper-item v-for="(item, index) in swiperList" :key="index">
|
||||
<view class="swiper-item">
|
||||
<image :src="item.image" mode="aspectFill" class="swiper-image" />
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<!-- 工作台 -->
|
||||
<view class="section">
|
||||
<view class="section-title">工作台</view>
|
||||
<view class="grid-container">
|
||||
<view
|
||||
class="grid-item"
|
||||
v-for="(item, index) in quickMenuList"
|
||||
:key="index"
|
||||
@click="handleMenuClick(item)"
|
||||
>
|
||||
<view class="item-icon" :style="{ background: item.bg }">
|
||||
<u-icon
|
||||
:name="item.icon"
|
||||
size="24"
|
||||
:color="item.color"
|
||||
/>
|
||||
</view>
|
||||
<view class="item-title">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import sheep from '@/sheep';
|
||||
|
||||
export default {
|
||||
onShow() {
|
||||
const userStore = sheep.$store('user');
|
||||
if (!userStore.isLogin) {
|
||||
sheep.$router.redirect('/pages/login/index');
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 轮播图数据
|
||||
swiperList: [
|
||||
{
|
||||
image: '/static/images/swiper/bg1.png',
|
||||
title: '轮播图1'
|
||||
},
|
||||
{
|
||||
image: '/static/images/swiper/bg2.png',
|
||||
title: '轮播图2'
|
||||
},
|
||||
{
|
||||
image: '/static/images/swiper/bg3.png',
|
||||
title: '轮播图3'
|
||||
}
|
||||
],
|
||||
|
||||
// 工作台功能数据
|
||||
quickMenuList: [
|
||||
{
|
||||
title: 'Demo',
|
||||
icon: 'file-text',
|
||||
color: '#fff',
|
||||
bg: '#3c9cff',
|
||||
path: '/pages/app/democontract/index'
|
||||
},
|
||||
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 处理菜单点击
|
||||
handleMenuClick(item) {
|
||||
sheep.$router.go(item.path);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.menu-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 轮播图样式 */
|
||||
.swiper-section {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.swiper-container {
|
||||
height: 300rpx;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.swiper-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.swiper-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 30rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 快捷功能网格布局 */
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20rpx;
|
||||
|
||||
.grid-item {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 24rpx;
|
||||
color: #606266;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 管理功能列表布局 */
|
||||
.list-container {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 30rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.item-icon-small {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
flex: 1;
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
color: #303133;
|
||||
margin-bottom: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
font-size: 22rpx;
|
||||
color: #909399;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media screen and (max-width: 750rpx) {
|
||||
.grid-container {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
padding: 24rpx 16rpx;
|
||||
|
||||
.item-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
51
pages/index/page.vue
Normal file
51
pages/index/page.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<!-- 自定义页面:支持装修 -->
|
||||
<template>
|
||||
<s-layout
|
||||
:title="state.name"
|
||||
navbar="custom"
|
||||
:bgStyle="state.page"
|
||||
:navbarStyle="state.navigationBar"
|
||||
onShareAppMessage
|
||||
showLeftButton
|
||||
>
|
||||
<s-block v-for="(item, index) in state.components" :key="index" :styles="item.property.style">
|
||||
<s-block-item :type="item.id" :data="item.property" :styles="item.property.style" />
|
||||
</s-block>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app';
|
||||
// Diy API removed with promotion module
|
||||
|
||||
const state = reactive({
|
||||
name: '',
|
||||
components: [],
|
||||
navigationBar: {},
|
||||
page: {},
|
||||
});
|
||||
onLoad(async (options) => {
|
||||
let id = options.id
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序预览自定义页面
|
||||
if (options.scene) {
|
||||
const sceneParams = decodeURIComponent(options.scene).split('=');
|
||||
id = sceneParams[1];
|
||||
}
|
||||
// #endif
|
||||
|
||||
const { code, data } = await DiyApi.getDiyPage(id);
|
||||
if (code === 0) {
|
||||
state.name = data.name;
|
||||
state.components = data.property?.components;
|
||||
state.navigationBar = data.property?.navigationBar;
|
||||
state.page = data.property?.page;
|
||||
}
|
||||
});
|
||||
|
||||
onPageScroll(() => {});
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
144
pages/index/search.vue
Normal file
144
pages/index/search.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<!-- 搜索界面 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#FFF' }" class="set-wrap" title="搜索">
|
||||
<view class="search-container">
|
||||
<view class="search-input-wrapper">
|
||||
<u-search
|
||||
v-model="searchText"
|
||||
placeholder="请输入关键字"
|
||||
:show-action="false"
|
||||
:focus="true"
|
||||
shape="square"
|
||||
@search="onSearch"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 搜索历史标题 -->
|
||||
<view class="search-section">
|
||||
<view class="section-header">
|
||||
<u-text text="搜索历史" size="16" bold color="#333" />
|
||||
<u-button
|
||||
text="清除搜索历史"
|
||||
size="mini"
|
||||
type="error"
|
||||
plain
|
||||
@click="onDelete"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 历史标签 -->
|
||||
<view class="history-tags" v-if="state.historyList.length">
|
||||
<u-tag
|
||||
v-for="(item, index) in state.historyList"
|
||||
:key="index"
|
||||
:text="item"
|
||||
type="info"
|
||||
plain
|
||||
size="medium"
|
||||
closable
|
||||
@click="onSearch(item)"
|
||||
@close="removeHistoryItem(index)"
|
||||
:custom-style="{ margin: '5rpx 10rpx 5rpx 0' }"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 无历史提示 -->
|
||||
<u-empty
|
||||
v-else
|
||||
mode="search"
|
||||
text="暂无搜索历史"
|
||||
textSize="14"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
|
||||
const searchText = ref('');
|
||||
const state = reactive({
|
||||
historyList: [],
|
||||
});
|
||||
|
||||
// 搜索
|
||||
function onSearch(keyword = '') {
|
||||
const searchKeyword = keyword || searchText.value;
|
||||
if (!searchKeyword) {
|
||||
return;
|
||||
}
|
||||
saveSearchHistory(searchKeyword);
|
||||
// 前往商品列表(带搜索条件)
|
||||
sheep.$router.go('/pages/goods/list', { keyword: searchKeyword });
|
||||
}
|
||||
|
||||
// 移除单个历史记录
|
||||
function removeHistoryItem(index) {
|
||||
state.historyList.splice(index, 1);
|
||||
uni.setStorageSync('searchHistory', state.historyList);
|
||||
}
|
||||
|
||||
// 保存搜索历史
|
||||
function saveSearchHistory(keyword) {
|
||||
// 如果关键词在搜索历史中,则把此关键词先移除
|
||||
if (state.historyList.includes(keyword)) {
|
||||
state.historyList.splice(state.historyList.indexOf(keyword), 1);
|
||||
}
|
||||
// 置顶关键词
|
||||
state.historyList.unshift(keyword);
|
||||
|
||||
// 最多保留 10 条记录
|
||||
if (state.historyList.length >= 10) {
|
||||
state.historyList.length = 10;
|
||||
}
|
||||
uni.setStorageSync('searchHistory', state.historyList);
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
uni.$u.modal({
|
||||
title: '提示',
|
||||
content: '确认清除搜索历史吗?',
|
||||
showCancelButton: true,
|
||||
confirmText: '确定',
|
||||
cancelText: '取消'
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
state.historyList = [];
|
||||
uni.removeStorageSync('searchHistory');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
state.historyList = uni.getStorageSync('searchHistory') || [];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-container {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.history-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
258
pages/index/user.vue
Normal file
258
pages/index/user.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<!-- 个人中心 -->
|
||||
<template>
|
||||
<s-layout
|
||||
title="我的"
|
||||
tabbar="/pages/index/user"
|
||||
navbar="custom"
|
||||
onShareAppMessage
|
||||
>
|
||||
<view class="user-container">
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="user-card">
|
||||
<view class="user-info">
|
||||
<view class="avatar-section" @click="handleAvatarClick">
|
||||
<s-avatar
|
||||
:src="userInfo.avatar"
|
||||
:nickname="userInfo.nickname || userInfo.username"
|
||||
:size="80"
|
||||
:bg-color="getAvatarBgColor(userInfo.nickname || userInfo.username)"
|
||||
/>
|
||||
<view class="user-details">
|
||||
<view class="user-name">{{ userInfo.nickname || userInfo.username || '未登录' }}</view>
|
||||
<view class="user-desc">{{ userInfo.mobile || '点击登录获取更多功能' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="menu-section">
|
||||
<view class="menu-group">
|
||||
<view class="group-title">账户管理</view>
|
||||
<view class="menu-list">
|
||||
<view class="menu-item" @click="goToUserInfo">
|
||||
<view class="item-left">
|
||||
<view class="item-icon" style="background: var(--ui-BG-Main-opacity-1);">
|
||||
<u-icon name="account" size="20" color="var(--ui-BG-Main)"/>
|
||||
</view>
|
||||
<view class="item-title">个人信息</view>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="16" color="#c0c4cc"/>
|
||||
</view>
|
||||
<view class="menu-item" @click="goToAbout">
|
||||
<view class="item-left">
|
||||
<view class="item-icon" style="background: var(--ui-BG-Main-opacity-1);">
|
||||
<u-icon name="info-circle" size="20" color="var(--ui-BG-Main)"/>
|
||||
</view>
|
||||
<view class="item-title">关于我们</view>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="16" color="#c0c4cc"/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录按钮 -->
|
||||
<view class="logout-section" v-if="isLogin">
|
||||
<view class="logout-btn" @click="handleLogout">
|
||||
退出登录
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { onShow, onPullDownRefresh } from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
import { getAvatarBgColor } from '@/sheep/utils/avatar.js';
|
||||
|
||||
// 用户信息
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
|
||||
// 页面事件
|
||||
onShow(() => {
|
||||
// 先检查登录状态,未登录则会自动跳转到登录页
|
||||
if (!sheep.$store('user').isLogin) {
|
||||
return;
|
||||
}
|
||||
// 已登录时更新用户数据
|
||||
sheep.$store('user').updateUserData();
|
||||
});
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
sheep.$store('user').updateUserData();
|
||||
setTimeout(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 头像点击事件
|
||||
function handleAvatarClick() {
|
||||
// 跳转到个人信息页面编辑头像
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/info'
|
||||
});
|
||||
}
|
||||
|
||||
// 方法
|
||||
function goToUserInfo() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/info'
|
||||
});
|
||||
}
|
||||
|
||||
function goToAbout() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/public/about'
|
||||
});
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
sheep.$store('user').logout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 用户信息卡片 */
|
||||
.user-card {
|
||||
background: var(--gradient-diagonal-primary, linear-gradient(135deg, #0055A2, rgba(0, 85, 162, 0.6)));
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 85, 162, 0.3);
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.user-details {
|
||||
margin-left: 20rpx;
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.user-desc {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
padding: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 菜单区域 */
|
||||
.menu-section {
|
||||
.menu-group {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.group-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 30rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.item-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 28rpx;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 退出登录按钮 */
|
||||
.logout-section {
|
||||
margin-top: 40rpx;
|
||||
|
||||
.logout-btn {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #f56c6c;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background-color: #fef0f0;
|
||||
transform: translateY(2rpx);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
342
pages/login/index.vue
Normal file
342
pages/login/index.vue
Normal file
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<s-layout class="login-container" title="登录/注册" :bgStyle="{
|
||||
background: '#fff'
|
||||
}">
|
||||
<view class="login-wrap">
|
||||
<!-- 1. 统一登录组件 (整合账号密码登录和短信登录) -->
|
||||
<unified-login
|
||||
v-if="authType === 'accountLogin' || authType === 'smsLogin'"
|
||||
:agreeStatus="state.protocol"
|
||||
@onConfirm="onConfirm"
|
||||
/>
|
||||
|
||||
<!-- 3. 忘记密码 resetPassword-->
|
||||
<!-- <reset-password v-if="authType === 'resetPassword'" /> -->
|
||||
|
||||
<!-- 4. 绑定手机号 changeMobile -->
|
||||
<change-mobile v-if="authType === 'changeMobile'" />
|
||||
|
||||
<!-- 5. 修改密码 changePassword-->
|
||||
<changePassword v-if="authType === 'changePassword'" />
|
||||
|
||||
<!-- 6. 微信小程序授权 -->
|
||||
<mp-authorization v-if="authType === 'mpAuthorization'" />
|
||||
|
||||
<!-- 7. 第三方登录 -->
|
||||
<view
|
||||
v-if="['accountLogin', 'smsLogin'].includes(authType)"
|
||||
class="auto-login-box ss-flex ss-flex-col ss-row-center ss-col-center"
|
||||
>
|
||||
<!-- 7.1 微信小程序的快捷登录 -->
|
||||
<view v-if="sheep.$platform.name === 'WechatMiniProgram'" class="ss-flex register-box">
|
||||
<view class="register-title">还没有账号?</view>
|
||||
<button
|
||||
class="ss-reset-button login-btn"
|
||||
open-type="getPhoneNumber"
|
||||
@getphonenumber="getPhoneNumber"
|
||||
style="color: var(--ui-BG-Main, #0055A2) !important"
|
||||
>
|
||||
快捷登录
|
||||
</button>
|
||||
<view class="circle" />
|
||||
</view>
|
||||
|
||||
<!-- 7.2 微信的公众号、App、小程序的登录,基于 openid + code -->
|
||||
<button
|
||||
v-if="
|
||||
['WechatOfficialAccount', 'WechatMiniProgram', 'App'].includes(sheep.$platform.name) &&
|
||||
sheep.$platform.isWechatInstalled
|
||||
"
|
||||
@tap="thirdLogin('wechat')"
|
||||
class="ss-reset-button auto-login-btn"
|
||||
>
|
||||
<image
|
||||
class="auto-login-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/wechat.png')"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- 7.3 iOS 登录 TODO 芋艿:等后面搞 App 再弄 -->
|
||||
<button
|
||||
v-if="sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
|
||||
@tap="thirdLogin('apple')"
|
||||
class="ss-reset-button auto-login-btn"
|
||||
>
|
||||
<image
|
||||
class="auto-login-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/apple.png')"
|
||||
/>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 用户协议的勾选 -->
|
||||
<view
|
||||
v-if="['accountLogin', 'smsLogin'].includes(authType)"
|
||||
class="agreement-box ss-flex ss-flex-col ss-col-center"
|
||||
:class="{ shake: currentProtocol }"
|
||||
>
|
||||
<view class="agreement-title ss-m-b-20">
|
||||
请阅读并同意以下协议:
|
||||
</view>
|
||||
|
||||
<view class="agreement-options-container">
|
||||
<view class="agreement-option" @tap="onAgree">
|
||||
<view class="radio-container ss-flex ss-col-center">
|
||||
<view
|
||||
class="custom-radio"
|
||||
:class="{ 'custom-radio-checked': state.protocol === true }"
|
||||
>
|
||||
<view v-if="state.protocol === true" class="radio-dot"></view>
|
||||
</view>
|
||||
<view class="agreement-text ss-flex ss-col-center ss-m-l-8">
|
||||
我已阅读并同意遵守
|
||||
<view class="tcp-text" @tap.stop="onProtocol('用户协议')"> 《用户协议》 </view>
|
||||
<view class="agreement-text">与</view>
|
||||
<view class="tcp-text" @tap.stop="onProtocol('隐私协议')"> 《隐私协议》 </view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="safe-box" />
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import unifiedLogin from '@/sheep/components/s-auth-modal/components/unified-login.vue';
|
||||
import changeMobile from '@/sheep/components/s-auth-modal/components/change-mobile.vue';
|
||||
import changePassword from '@/sheep/components/s-auth-modal/components/change-password.vue';
|
||||
import mpAuthorization from '@/sheep/components/s-auth-modal/components/mp-authorization.vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { navigateAfterLogin } from '@/sheep/helper/login-redirect';
|
||||
|
||||
const authType = ref('accountLogin');
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.authType) {
|
||||
authType.value = options.authType;
|
||||
}
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
protocol: false, // false 表示未勾选,true 表示已同意
|
||||
});
|
||||
|
||||
const currentProtocol = ref(false);
|
||||
|
||||
// 同意协议
|
||||
function onAgree() {
|
||||
state.protocol = !state.protocol;
|
||||
uni.showToast({
|
||||
title: state.protocol ? '已勾选协议' : '已取消勾选',
|
||||
icon: state.protocol ? 'success' : 'none',
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
|
||||
// 查看协议
|
||||
function onProtocol(title) {
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title,
|
||||
});
|
||||
}
|
||||
|
||||
// 点击登录 / 注册事件
|
||||
function onConfirm(e) {
|
||||
currentProtocol.value = e;
|
||||
setTimeout(() => {
|
||||
currentProtocol.value = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 第三方授权登陆(微信小程序、Apple)
|
||||
const thirdLogin = async (provider) => {
|
||||
if (state.protocol !== true) {
|
||||
currentProtocol.value = true;
|
||||
setTimeout(() => {
|
||||
currentProtocol.value = false;
|
||||
}, 1000);
|
||||
|
||||
sheep.$helper.toast('请先勾选协议');
|
||||
return;
|
||||
}
|
||||
const loginRes = await sheep.$platform.useProvider(provider).login();
|
||||
if (loginRes) {
|
||||
const userInfo = await sheep.$store('user').getInfo();
|
||||
// 如果用户已经有头像和昵称,不需要再次授权
|
||||
if (userInfo.avatar && userInfo.nickname) {
|
||||
// 登录成功后跳转到首页
|
||||
navigateAfterLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
// 触发小程序授权信息弹框
|
||||
// #ifdef MP-WEIXIN
|
||||
authType.value = 'mpAuthorization';
|
||||
// #endif
|
||||
}
|
||||
};
|
||||
|
||||
// 微信小程序的“手机号快速验证”:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
|
||||
const getPhoneNumber = async (e) => {
|
||||
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||||
sheep.$helper.toast('快捷登录失败');
|
||||
return;
|
||||
}
|
||||
let result = await sheep.$platform.useProvider().mobileLogin(e.detail);
|
||||
if (result) {
|
||||
// 登录成功后跳转到首页
|
||||
navigateAfterLogin();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/sheep/components/s-auth-modal/index.scss';
|
||||
.login-container {
|
||||
.login-wrap {
|
||||
padding-top: 100rpx;
|
||||
}
|
||||
}
|
||||
.shake {
|
||||
animation: shake 0.05s linear 4 alternate;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
from {
|
||||
transform: translateX(-10rpx);
|
||||
}
|
||||
to {
|
||||
transform: translateX(10rpx);
|
||||
}
|
||||
}
|
||||
|
||||
.register-box {
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
.register-btn {
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.register-title {
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
.or-title {
|
||||
margin: 0 16rpx;
|
||||
color: #999999;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
.login-btn {
|
||||
color: var(--ui-BG-Main, #0055A2) !important;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.circle {
|
||||
position: absolute;
|
||||
right: 0rpx;
|
||||
top: 18rpx;
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
background: var(--ui-BG-Main, #0055A2) !important;
|
||||
}
|
||||
}
|
||||
.safe-box {
|
||||
height: calc(constant(safe-area-inset-bottom) / 5 * 3);
|
||||
height: calc(env(safe-area-inset-bottom) / 5 * 3);
|
||||
}
|
||||
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main, #0055A2) !important;
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: $dark-9;
|
||||
}
|
||||
|
||||
.agreement-title {
|
||||
font-size: 28rpx;
|
||||
color: $dark-9;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
padding-left: 60rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.protocol-status {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
margin-top: 8rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 16rpx;
|
||||
display: inline-block;
|
||||
|
||||
&.protocol-agreed {
|
||||
color: #52c41a;
|
||||
background-color: rgba(82, 196, 26, 0.1);
|
||||
}
|
||||
|
||||
&.protocol-refused {
|
||||
color: #ff4d4f;
|
||||
background-color: rgba(255, 77, 79, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-options-container {
|
||||
width: 100%;
|
||||
padding-left: 100rpx;
|
||||
}
|
||||
|
||||
.agreement-option {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 自定义radio样式 - 同意 */
|
||||
.custom-radio {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border: 2rpx solid #d9d9d9;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.custom-radio-checked {
|
||||
border-color: var(--ui-BG-Main, #409eff);
|
||||
background-color: var(--ui-BG-Main, #409eff);
|
||||
}
|
||||
}
|
||||
|
||||
.radio-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
540
pages/public/about.vue
Normal file
540
pages/public/about.vue
Normal file
@@ -0,0 +1,540 @@
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#f5f7fa' }" class="about-page" title="关于我们">
|
||||
<u-card :show-head="false" margin="20rpx" padding="40rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<view class="hero-content">
|
||||
<u-avatar
|
||||
v-if="appInfo.logo"
|
||||
:src="sheep.$url.cdn(appInfo.logo)"
|
||||
size="120"
|
||||
bg-color="transparent"
|
||||
class="hero-logo"
|
||||
/>
|
||||
<u-text
|
||||
:text="displayName"
|
||||
type="primary"
|
||||
size="40rpx"
|
||||
bold
|
||||
color="#fff"
|
||||
class="hero-name"
|
||||
/>
|
||||
<u-text
|
||||
:text="`${displayName} 致力于用开源的数字化能力,帮助企业快速构建电商、营销与业务协同平台。`"
|
||||
size="28rpx"
|
||||
color="rgba(255,255,255,0.9)"
|
||||
class="hero-slogan"
|
||||
/>
|
||||
<view class="hero-tags">
|
||||
<u-tag
|
||||
v-for="tag in heroTags"
|
||||
:key="tag"
|
||||
:text="tag"
|
||||
bg-color="rgba(255,255,255,0.16)"
|
||||
color="#fff"
|
||||
size="mini"
|
||||
shape="circle"
|
||||
plain
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-row gutter="16" class="metrics-row">
|
||||
<u-col span="4" v-for="metric in metrics" :key="metric.title">
|
||||
<u-card :show-head="false" margin="0" padding="32rpx 24rpx" border-radius="20rpx">
|
||||
<template #body>
|
||||
<view class="metric-content">
|
||||
<u-text
|
||||
:text="metric.value"
|
||||
size="48rpx"
|
||||
bold
|
||||
type="primary"
|
||||
class="metric-value"
|
||||
/>
|
||||
<u-text
|
||||
:text="metric.title"
|
||||
size="26rpx"
|
||||
bold
|
||||
color="#303133"
|
||||
class="metric-title"
|
||||
/>
|
||||
<u-text
|
||||
:text="metric.desc"
|
||||
size="22rpx"
|
||||
color="#909399"
|
||||
class="metric-desc"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
</u-col>
|
||||
</u-row>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="我们提供什么" size="32rpx" bold color="#303133" class="section-title" />
|
||||
<u-text text="围绕企业全链路经营的核心能力" size="24rpx" color="#909399" class="section-subtitle" />
|
||||
|
||||
<view class="capability-grid">
|
||||
<u-card
|
||||
v-for="item in capabilities"
|
||||
:key="item.title"
|
||||
:show-head="false"
|
||||
margin="0 0 24rpx 0"
|
||||
padding="28rpx"
|
||||
border-radius="20rpx"
|
||||
bg-color="linear-gradient(135deg, rgba(245, 247, 250, 1), rgba(245, 247, 250, 0.6))"
|
||||
>
|
||||
<template #body>
|
||||
<u-text :text="item.title" size="30rpx" bold color="#2c3e50" class="capability-title" />
|
||||
<u-text :text="item.desc" size="24rpx" color="#606266" class="capability-desc" />
|
||||
<view class="capability-tags">
|
||||
<u-tag
|
||||
v-for="tag in item.tags"
|
||||
:key="tag"
|
||||
:text="tag"
|
||||
bg-color="rgba(0, 85, 162, 0.08)"
|
||||
color="var(--ui-BG-Main)"
|
||||
size="mini"
|
||||
shape="circle"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="我们的理念" size="32rpx" bold color="#303133" class="section-title" />
|
||||
|
||||
<view class="value-list">
|
||||
<u-card
|
||||
v-for="item in values"
|
||||
:key="item.title"
|
||||
:show-head="false"
|
||||
margin="0 0 24rpx 0"
|
||||
padding="24rpx 26rpx"
|
||||
border-radius="20rpx"
|
||||
bg-color="rgba(0, 85, 162, 0.05)"
|
||||
>
|
||||
<template #body>
|
||||
<u-text :text="item.title" size="28rpx" bold color="#2c3e50" class="value-title" />
|
||||
<u-text :text="item.desc" size="24rpx" color="#606266" class="value-desc" />
|
||||
</template>
|
||||
</u-card>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="发展里程碑" size="32rpx" bold color="#303133" class="section-title" />
|
||||
|
||||
<view class="timeline">
|
||||
<view
|
||||
class="timeline-item"
|
||||
v-for="(item, index) in milestones"
|
||||
:key="item.year"
|
||||
>
|
||||
<view class="timeline-dot"></view>
|
||||
<view class="timeline-content">
|
||||
<u-text :text="item.year" size="28rpx" bold color="#2c3e50" class="timeline-year" />
|
||||
<u-text :text="item.title" size="26rpx" bold color="#303133" class="timeline-title" />
|
||||
<u-text :text="item.desc" size="24rpx" color="#606266" class="timeline-desc" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-card :show-head="false" margin="20rpx" padding="32rpx 28rpx" border-radius="24rpx">
|
||||
<template #body>
|
||||
<u-text text="联系与合作" size="32rpx" bold color="#303133" class="section-title" />
|
||||
<u-text text="欢迎与我们探讨更多共赢的可能" size="24rpx" color="#909399" class="section-subtitle" />
|
||||
|
||||
<view class="contact-list">
|
||||
<u-cell
|
||||
v-for="item in contacts"
|
||||
:key="item.label"
|
||||
:title="item.label"
|
||||
:label="item.value"
|
||||
:is-link="true"
|
||||
arrow-direction="right"
|
||||
bg-color="rgba(245, 247, 250, 0.8)"
|
||||
title-style="font-size: 26rpx; font-weight: 600; color: #2c3e50;"
|
||||
label-style="font-size: 24rpx; color: #606266; word-break: break-all; line-height: 34rpx;"
|
||||
@click="handleContact(item)"
|
||||
class="contact-cell"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-card>
|
||||
|
||||
<u-text
|
||||
text="移动商城 始终坚持开源共享,与开发者和企业伙伴一起打造可持续的数字化生态。"
|
||||
size="24rpx"
|
||||
color="#909399"
|
||||
align="center"
|
||||
class="footer-hint"
|
||||
/>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const appInfo = computed(() => sheep.$store('app').info || {});
|
||||
const displayName = computed(() => appInfo.value.name || '移动商城');
|
||||
|
||||
const heroTags = [
|
||||
'100% 开源可商用',
|
||||
'支持多端统一交付',
|
||||
'沉淀企业级最佳实践'
|
||||
];
|
||||
|
||||
const metrics = [
|
||||
{
|
||||
title: '开源历程',
|
||||
value: '7+',
|
||||
desc: '年持续迭代与社区共建'
|
||||
},
|
||||
{
|
||||
title: '行业方案',
|
||||
value: '20+',
|
||||
desc: '覆盖零售、制造、政企等场景'
|
||||
},
|
||||
{
|
||||
title: '交付效率',
|
||||
value: '30%',
|
||||
desc: '平均缩短客户上线周期'
|
||||
}
|
||||
];
|
||||
|
||||
const capabilities = [
|
||||
{
|
||||
title: '产品矩阵',
|
||||
desc: '以电商中台为核心,延展直播带货、会员营销、全渠道订单与履约管理。',
|
||||
tags: ['商城交易', '营销裂变', '数据运营']
|
||||
},
|
||||
{
|
||||
title: '技术架构',
|
||||
desc: '基于 Spring Cloud Alibaba + Vue3 + UniApp,支持多租户、微服务与多端同源。',
|
||||
tags: ['微服务', '多端一致', '低代码装修']
|
||||
},
|
||||
{
|
||||
title: '服务体系',
|
||||
desc: '提供从咨询、定制、交付到长期运营的全生命周期陪伴式服务。',
|
||||
tags: ['数字化顾问', '驻场交付', '持续运营']
|
||||
}
|
||||
];
|
||||
|
||||
const values = [
|
||||
{
|
||||
title: '使命',
|
||||
desc: '让每一家企业都能以更低成本、更快速度完成业务数字化升级。'
|
||||
},
|
||||
{
|
||||
title: '愿景',
|
||||
desc: '打造开放、可信赖、可持续的企业级数字商业生态。'
|
||||
},
|
||||
{
|
||||
title: '价值观',
|
||||
desc: '客户成功、极致体验、持续创新、坦诚协作。'
|
||||
}
|
||||
];
|
||||
|
||||
const milestones = [
|
||||
{
|
||||
year: '2018',
|
||||
title: '开源起航',
|
||||
desc: '发布首个电商项目原型,与社区开发者共同启动开源计划。'
|
||||
},
|
||||
{
|
||||
year: '2020',
|
||||
title: '企业级升级',
|
||||
desc: '引入多租户、权限与大促能力,满足成长型企业核心诉求。'
|
||||
},
|
||||
{
|
||||
year: '2022',
|
||||
title: '多端一体',
|
||||
desc: '统一 H5、App 与小程序技术栈,实现一次开发,多端交付。'
|
||||
},
|
||||
{
|
||||
year: '2024',
|
||||
title: '智能加速',
|
||||
desc: '引入智能客服、营销推荐等 AI 能力,提升运营效率。'
|
||||
},
|
||||
{
|
||||
year: '2025',
|
||||
title: '生态共建',
|
||||
desc: '联合生态伙伴共建行业方案,形成开放合作的生态体系。'
|
||||
}
|
||||
];
|
||||
|
||||
const contacts = [
|
||||
{
|
||||
label: '商务合作',
|
||||
value: 'business@iocoder.cn',
|
||||
type: 'email'
|
||||
},
|
||||
{
|
||||
label: '客服热线',
|
||||
value: '400-860-888',
|
||||
type: 'phone',
|
||||
actionValue: '400860888'
|
||||
},
|
||||
{
|
||||
label: '开源仓库',
|
||||
value: 'https://github.com/YunaiV/ruoyi-vue-pro',
|
||||
type: 'link'
|
||||
},
|
||||
{
|
||||
label: '文档中心',
|
||||
value: 'https://doc.iocoder.cn',
|
||||
type: 'link'
|
||||
},
|
||||
{
|
||||
label: '办公地址',
|
||||
value: '浙江省杭州市滨江区江南大道 777 号数字产业园',
|
||||
type: 'text'
|
||||
}
|
||||
];
|
||||
|
||||
function handleContact(item) {
|
||||
if (item.type === 'phone') {
|
||||
const phoneNumber = item.actionValue || item.value.replace(/[^0-9]/g, '');
|
||||
if (!phoneNumber) {
|
||||
return;
|
||||
}
|
||||
uni.makePhoneCall({
|
||||
phoneNumber,
|
||||
fail: () => {
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '号码已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.type === 'link') {
|
||||
sheep.$router.go('/pages/public/webview', {
|
||||
title: item.label,
|
||||
url: encodeURIComponent(item.value)
|
||||
});
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '链接已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.type === 'text') {
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '地址已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uni.setClipboardData({
|
||||
data: item.value,
|
||||
success: () => {
|
||||
uni.showToast({ title: '信息已复制', icon: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.about-page {
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
background: #f5f7fa;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// Hero card 渐变背景
|
||||
:deep(.u-card) {
|
||||
&:first-child {
|
||||
background: linear-gradient(135deg, rgba(0, 85, 162, 0.95), rgba(0, 85, 162, 0.68));
|
||||
box-shadow: 0 16rpx 36rpx rgba(0, 85, 162, 0.25);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
|
||||
.hero-logo {
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 24rpx;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.hero-slogan {
|
||||
margin-bottom: 28rpx;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
|
||||
.hero-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.metrics-row {
|
||||
margin: 20rpx;
|
||||
}
|
||||
|
||||
.metric-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
|
||||
.metric-value {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.metric-title {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.metric-desc {
|
||||
line-height: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.capability-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.capability-title {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.capability-desc {
|
||||
margin-bottom: 16rpx;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
|
||||
.capability-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.value-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.value-title {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.value-desc {
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
margin-left: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
border-left: 2rpx solid rgba(0, 85, 162, 0.2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32rpx;
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
left: -31rpx;
|
||||
top: 6rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: var(--ui-BG-Main);
|
||||
box-shadow: 0 0 0 6rpx rgba(0, 85, 162, 0.15);
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
padding-left: 12rpx;
|
||||
|
||||
.timeline-year {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.timeline-desc {
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contact-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.contact-cell {
|
||||
border-radius: 18rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.footer-hint {
|
||||
padding: 24rpx 36rpx 80rpx;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
@media (min-width: 750px) {
|
||||
.metrics-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.capability-grid,
|
||||
.value-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
60
pages/public/error.vue
Normal file
60
pages/public/error.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<!-- 错误界面 -->
|
||||
<template>
|
||||
<view class="error-page">
|
||||
<s-empty
|
||||
v-if="errCode === 'NetworkError'"
|
||||
icon="/static/internet-empty.png"
|
||||
text="网络连接失败"
|
||||
showAction
|
||||
actionText="重新连接"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
/>
|
||||
<s-empty
|
||||
v-else-if="errCode === 'TemplateError'"
|
||||
icon="/static/internet-empty.png"
|
||||
text="未找到模板,请前往后台启用对应模板"
|
||||
showAction
|
||||
actionText="重新加载"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
/>
|
||||
<s-empty
|
||||
v-else-if="errCode !== ''"
|
||||
icon="/static/internet-empty.png"
|
||||
:text="errMsg"
|
||||
showAction
|
||||
actionText="重新加载"
|
||||
@clickAction="onReconnect"
|
||||
buttonColor="#ff3000"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ref } from 'vue';
|
||||
import { ShoproInit } from '@/sheep';
|
||||
|
||||
const errCode = ref('');
|
||||
const errMsg = ref('');
|
||||
|
||||
onLoad((options) => {
|
||||
errCode.value = options.errCode;
|
||||
errMsg.value = options.errMsg;
|
||||
});
|
||||
|
||||
// 重新连接
|
||||
async function onReconnect() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/menu',
|
||||
});
|
||||
await ShoproInit();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.error-page {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
15
pages/public/faq.vue
Normal file
15
pages/public/faq.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- 页面已移除 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#FFF' }" class="set-wrap" title="页面已下线">
|
||||
<s-empty text="该页面已下线" icon="/static/internet-empty.png" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.set-wrap {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
45
pages/public/richtext.vue
Normal file
45
pages/public/richtext.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<!-- 文章展示 -->
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#FFF' }" :title="state.title" class="set-wrap">
|
||||
<view class="ss-p-30 richtext"><mp-html :content="state.content"></mp-html></view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { reactive } from 'vue';
|
||||
// Article API removed with promotion module
|
||||
|
||||
const state = reactive({
|
||||
title: '',
|
||||
content: '',
|
||||
});
|
||||
|
||||
async function getRichTextContent(id, title) {
|
||||
// Article API has been removed as it was part of promotion module
|
||||
state.title = title || '内容';
|
||||
state.content = '暂无内容';
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.title) {
|
||||
state.title = options.title;
|
||||
uni.setNavigationBarTitle({
|
||||
title: state.title,
|
||||
});
|
||||
}
|
||||
getRichTextContent(options.id, options.title);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.set-title {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
:deep() {
|
||||
image {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
236
pages/public/setting.vue
Normal file
236
pages/public/setting.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<s-layout :bgStyle="{ color: '#fff' }" class="set-wrap" title="系统设置">
|
||||
<view class="header-box ss-flex-col ss-row-center ss-col-center">
|
||||
<image
|
||||
class="logo-img ss-m-b-46"
|
||||
:src="sheep.$url.cdn(appInfo.logo)"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<view class="name ss-m-b-24">{{ appInfo.name }}</view>
|
||||
</view>
|
||||
|
||||
<view class="container-list">
|
||||
<uni-list :border="false">
|
||||
<uni-list-item
|
||||
title="当前版本"
|
||||
:rightText="appInfo.version"
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@tap="onCheckUpdate"
|
||||
/>
|
||||
<uni-list-item
|
||||
title="本地缓存"
|
||||
:rightText="storageSize"
|
||||
showArrow
|
||||
:border="false"
|
||||
class="list-border"
|
||||
/>
|
||||
<uni-list-item
|
||||
title="关于我们"
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title: '关于我们'
|
||||
})
|
||||
"
|
||||
/>
|
||||
<!-- 为了过审 只有 iOS-App 有注销账号功能 -->
|
||||
<uni-list-item
|
||||
v-if="isLogin && sheep.$platform.os === 'ios' && sheep.$platform.name === 'App'"
|
||||
title="注销账号"
|
||||
rightText=""
|
||||
showArrow
|
||||
clickable
|
||||
:border="false"
|
||||
class="list-border"
|
||||
@click="onLogoff"
|
||||
/>
|
||||
</uni-list>
|
||||
</view>
|
||||
<view class="set-footer ss-flex-col ss-row-center ss-col-center">
|
||||
<view class="agreement-box ss-flex ss-col-center ss-m-b-40">
|
||||
<view class="ss-flex ss-col-center ss-m-b-10">
|
||||
<view
|
||||
class="tcp-text"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title: '用户协议'
|
||||
})
|
||||
"
|
||||
>
|
||||
《用户协议》
|
||||
</view>
|
||||
<view class="agreement-text">与</view>
|
||||
<view
|
||||
class="tcp-text"
|
||||
@tap="
|
||||
sheep.$router.go('/pages/public/richtext', {
|
||||
title: '隐私协议'
|
||||
})
|
||||
"
|
||||
>
|
||||
《隐私协议》
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="copyright-text ss-m-b-10">{{ appInfo.copyright }}</view>
|
||||
<view class="copyright-text">{{ appInfo.copytime }}</view>
|
||||
</view>
|
||||
<su-fixed bottom placeholder>
|
||||
<view class="ss-p-x-20 ss-p-b-40">
|
||||
<button
|
||||
class="loginout-btn ss-reset-button ui-BG-Main ui-Shadow-Main"
|
||||
@tap="onLogout"
|
||||
v-if="isLogin"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import sheep from '@/sheep';
|
||||
import { computed, reactive } from 'vue';
|
||||
import AuthUtil from '@/sheep/api/system/auth';
|
||||
|
||||
const appInfo = computed(() => sheep.$store('app').info);
|
||||
const isLogin = computed(() => sheep.$store('user').isLogin);
|
||||
const storageSize = uni.getStorageInfoSync().currentSize + 'Kb';
|
||||
const state = reactive({
|
||||
showModal: false,
|
||||
});
|
||||
|
||||
function onCheckUpdate() {
|
||||
sheep.$platform.checkUpdate();
|
||||
// 小程序初始化时已检查更新
|
||||
// H5实时更新无需检查
|
||||
// App 1.跳转应用市场更新 2.手动热更新 3.整包更新
|
||||
}
|
||||
|
||||
// 注销账号
|
||||
function onLogoff() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认注销账号?',
|
||||
success: async function (res) {
|
||||
if (!res.confirm) {
|
||||
return;
|
||||
}
|
||||
const { code } = await AuthUtil.logout();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
sheep.$store('user').logout();
|
||||
sheep.$router.go('/pages/index/user');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 退出账号
|
||||
function onLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确认退出账号?',
|
||||
success: async function (res) {
|
||||
if (!res.confirm) {
|
||||
return;
|
||||
}
|
||||
const { code } = await AuthUtil.logout();
|
||||
if (code !== 0) {
|
||||
return;
|
||||
}
|
||||
sheep.$store('user').logout();
|
||||
sheep.$router.go('/pages/index/user');
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.set-title {
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.header-box {
|
||||
padding: 100rpx 0;
|
||||
|
||||
.logo-img {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 42rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-3;
|
||||
}
|
||||
|
||||
.version {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
line-height: 32rpx;
|
||||
color: $gray-b;
|
||||
}
|
||||
}
|
||||
|
||||
.set-footer {
|
||||
margin: 100rpx 0 0 0;
|
||||
|
||||
.copyright-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: $gray-c;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.agreement-box {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
|
||||
.tcp-text {
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loginout-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.list-border {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
border-bottom: 2rpx solid #eeeeee;
|
||||
}
|
||||
|
||||
:deep(.uni-list-item__content-title) {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(.uni-list-item__extra-text) {
|
||||
color: #bbbbbb;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
18
pages/public/webview.vue
Normal file
18
pages/public/webview.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<!-- 网页加载 -->
|
||||
<template>
|
||||
<view>
|
||||
<web-view :src="url" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const url = ref('');
|
||||
onLoad((options) => {
|
||||
url.value = decodeURIComponent(options.url);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
410
pages/user/info.vue
Normal file
410
pages/user/info.vue
Normal file
@@ -0,0 +1,410 @@
|
||||
<!-- 用户信息 -->
|
||||
<template>
|
||||
<s-layout title="用户信息" class="set-userinfo-wrap">
|
||||
<uni-forms
|
||||
:model="state.model"
|
||||
:rules="state.rules"
|
||||
labelPosition="left"
|
||||
border
|
||||
class="form-box"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<view class="ss-flex ss-row-center ss-col-center ss-p-t-60 ss-p-b-0 bg-white">
|
||||
<view class="header-box-content">
|
||||
<s-avatar
|
||||
:src="state.model?.avatar"
|
||||
:nickname="state.model?.nickname || state.model?.username"
|
||||
:size="160"
|
||||
:editable="true"
|
||||
:bg-color="getAvatarBgColor(state.model?.nickname || state.model?.username)"
|
||||
@click="onChangeAvatar"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bg-white ss-p-x-30">
|
||||
<!-- 昵称 + 性别 -->
|
||||
<uni-forms-item name="nickname" label="昵称">
|
||||
<uni-easyinput
|
||||
v-model="state.model.nickname"
|
||||
type="nickname"
|
||||
placeholder="设置昵称"
|
||||
:inputBorder="false"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item name="sex" label="性别">
|
||||
<view class="ss-flex ss-col-center ss-h-100">
|
||||
<radio-group @change="onChangeGender" class="ss-flex ss-col-center">
|
||||
<label class="radio" v-for="item in sexRadioMap" :key="item.value">
|
||||
<view class="ss-flex ss-col-center ss-m-r-32">
|
||||
<radio
|
||||
:value="item.value"
|
||||
color="var(--ui-BG-Main)"
|
||||
style="transform: scale(0.8)"
|
||||
:checked="parseInt(item.value) === state.model?.sex"
|
||||
/>
|
||||
<view class="gender-name">{{ item.name }}</view>
|
||||
</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item name="mobile" label="手机号" @tap="onChangeMobile">
|
||||
<uni-easyinput
|
||||
v-model="userInfo.mobile"
|
||||
placeholder="请绑定手机号"
|
||||
:inputBorder="false"
|
||||
disabled
|
||||
:styles="{ disableColor: '#fff' }"
|
||||
:placeholderStyle="placeholderStyle"
|
||||
:clearable="false"
|
||||
>
|
||||
<template v-slot:right>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<su-radio v-if="userInfo.verification?.mobile" :modelValue="true" />
|
||||
<button v-else class="ss-reset-button ss-flex ss-col-center ss-row-center">
|
||||
<text class="_icon-forward" style="color: #bbbbbb; font-size: 26rpx"></text>
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
</uni-easyinput>
|
||||
</uni-forms-item>
|
||||
|
||||
</view>
|
||||
</uni-forms>
|
||||
|
||||
<!-- 当前社交平台的绑定关系,只处理 wechat 微信场景 -->
|
||||
<view v-if="sheep.$platform.name !== 'H5'">
|
||||
<view class="title-box ss-p-l-30">第三方账号绑定</view>
|
||||
<view class="account-list ss-flex ss-row-between">
|
||||
<view v-if="'WechatOfficialAccount' === sheep.$platform.name" class="ss-flex ss-col-center">
|
||||
<image
|
||||
class="list-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/WechatOfficialAccount.png')"
|
||||
/>
|
||||
<text class="list-name">微信公众号</text>
|
||||
</view>
|
||||
<view v-if="'WechatMiniProgram' === sheep.$platform.name" class="ss-flex ss-col-center">
|
||||
<image
|
||||
class="list-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/WechatMiniProgram.png')"
|
||||
/>
|
||||
<text class="list-name">微信小程序</text>
|
||||
</view>
|
||||
<view v-if="'App' === sheep.$platform.name" class="ss-flex ss-col-center">
|
||||
<image
|
||||
class="list-img"
|
||||
:src="sheep.$url.static('/static/img/shop/platform/wechat.png')"
|
||||
/>
|
||||
<text class="list-name">微信开放平台</text>
|
||||
</view>
|
||||
<view class="ss-flex ss-col-center">
|
||||
<view class="info ss-flex ss-col-center" v-if="state.thirdInfo">
|
||||
<image class="avatar ss-m-r-20" :src="sheep.$url.cdn(state.thirdInfo.avatar)" />
|
||||
<text class="name">{{ state.thirdInfo.nickname }}</text>
|
||||
</view>
|
||||
<view class="bind-box ss-m-l-20">
|
||||
<button
|
||||
v-if="state.thirdInfo.openid"
|
||||
class="ss-reset-button relieve-btn"
|
||||
@tap="unBindThirdOauth"
|
||||
>
|
||||
解绑
|
||||
</button>
|
||||
<button v-else class="ss-reset-button bind-btn" @tap="bindThirdOauth">绑定</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<su-fixed bottom placeholder bg="none">
|
||||
<view class="footer-box ss-p-20">
|
||||
<button class="ss-rest-button logout-btn ui-Shadow-Main" @tap="onSubmit">保存</button>
|
||||
</view>
|
||||
</su-fixed>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, onBeforeMount } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { clone } from 'lodash-es';
|
||||
import UserApi from '@/sheep/api/system/user';
|
||||
import { getAvatarBgColor } from '@/sheep/utils/avatar.js';
|
||||
import {
|
||||
chooseAndUploadFile,
|
||||
uploadFilesFromPath,
|
||||
} from '@/sheep/components/s-uploader/choose-and-upload-file';
|
||||
|
||||
const state = reactive({
|
||||
model: {}, // 个人信息
|
||||
rules: {},
|
||||
thirdInfo: {}, // 社交用户的信息
|
||||
});
|
||||
|
||||
const placeholderStyle = 'color:#BBBBBB;font-size:28rpx;line-height:normal';
|
||||
|
||||
const sexRadioMap = [
|
||||
{
|
||||
name: '男',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
name: '女',
|
||||
value: '2',
|
||||
},
|
||||
];
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
// 选择性别
|
||||
function onChangeGender(e) {
|
||||
state.model.sex = e.detail.value;
|
||||
}
|
||||
|
||||
// 修改手机号
|
||||
const onChangeMobile = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/index?authType=changeMobile'
|
||||
});
|
||||
};
|
||||
|
||||
// 选择微信的头像,进行上传
|
||||
async function onChooseAvatar(e) {
|
||||
debugger;
|
||||
const tempUrl = e.detail.avatarUrl || '';
|
||||
if (!tempUrl) return;
|
||||
const files = await uploadFilesFromPath(tempUrl);
|
||||
if (files.length > 0) {
|
||||
state.model.avatar = files[0].url;
|
||||
}
|
||||
}
|
||||
|
||||
// 手动选择头像,进行上传
|
||||
async function onChangeAvatar() {
|
||||
const files = await chooseAndUploadFile({ type: 'image' });
|
||||
if (files.length > 0) {
|
||||
state.model.avatar = files[0].url;
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定第三方账号
|
||||
async function bindThirdOauth() {
|
||||
let result = await sheep.$platform.useProvider('wechat').bind();
|
||||
if (result) {
|
||||
await getUserInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// 解绑第三方账号
|
||||
function unBindThirdOauth() {
|
||||
uni.showModal({
|
||||
title: '解绑提醒',
|
||||
content: '解绑后您将无法通过微信登录此账号',
|
||||
cancelText: '再想想',
|
||||
confirmText: '确定',
|
||||
success: async function (res) {
|
||||
if (!res.confirm) {
|
||||
return;
|
||||
}
|
||||
const result = await sheep.$platform.useProvider('wechat').unbind(state.thirdInfo.openid);
|
||||
if (result) {
|
||||
await getUserInfo();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 保存信息
|
||||
async function onSubmit() {
|
||||
const { code } = await UserApi.updateUser({
|
||||
avatar: state.model.avatar,
|
||||
nickname: state.model.nickname,
|
||||
sex: state.model.sex,
|
||||
});
|
||||
if (code === 0) {
|
||||
await getUserInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// 获得用户信息
|
||||
const getUserInfo = async () => {
|
||||
// 个人信息
|
||||
const userInfo = await sheep.$store('user').getInfo();
|
||||
state.model = clone(userInfo);
|
||||
|
||||
// 获得社交用户的信息
|
||||
if (sheep.$platform.name !== 'H5') {
|
||||
const result = await sheep.$platform.useProvider('wechat').getInfo();
|
||||
state.thirdInfo = result || {};
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
getUserInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.uni-file-picker {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.uni-file-picker__container {
|
||||
margin: -14rpx -12rpx;
|
||||
}
|
||||
|
||||
.file-picker__progress {
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
.uni-list-item__content-title {
|
||||
font-size: 28rpx !important;
|
||||
color: #333333 !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
|
||||
.uni-icons {
|
||||
font-size: 40rpx !important;
|
||||
}
|
||||
|
||||
.is-disabled {
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.disabled) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.gender-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: normal;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.title-box {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
line-height: 100rpx;
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 710rpx;
|
||||
height: 80rpx;
|
||||
background: var(--gradient-horizontal-primary, linear-gradient(90deg, #0055A2, rgba(0, 85, 162, 0.6)));
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background: linear-gradient(90deg, #003F73, rgba(0, 63, 115, 0.6));
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
.radio-dark {
|
||||
filter: grayscale(100%);
|
||||
filter: gray;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.content-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.header-box-content {
|
||||
position: relative;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.avatar-action {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
width: 160rpx;
|
||||
height: 46rpx;
|
||||
background: rgba(#000000, 0.3);
|
||||
|
||||
.avatar-action-btn {
|
||||
width: 160rpx;
|
||||
height: 46rpx;
|
||||
font-weight: 500;
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定项
|
||||
.account-list {
|
||||
background-color: $white;
|
||||
height: 100rpx;
|
||||
padding: 0 20rpx;
|
||||
|
||||
.list-img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.list-name {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.info {
|
||||
.avatar {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: $dark-9;
|
||||
}
|
||||
}
|
||||
|
||||
.bind-box {
|
||||
width: 100rpx;
|
||||
height: 50rpx;
|
||||
line-height: normal;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
|
||||
.bind-btn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 25rpx;
|
||||
background: #f4f4f4;
|
||||
color: #999999;
|
||||
}
|
||||
.relieve-btn {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 25rpx;
|
||||
background: var(--ui-BG-Main-opacity-1);
|
||||
color: var(--ui-BG-Main);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user