完善页面7

This commit is contained in:
2026-02-13 01:52:47 +08:00
parent da1075b9e9
commit f814db2f12
4 changed files with 799 additions and 89 deletions

View File

@@ -63,7 +63,8 @@
<!-- 内容展示区 (内部路由渲染) -->
<view class="content-scroll">
<view class="content-inner" :class="{ 'is-mobile': isMobile }">
<component :is="currentComponent" v-if="!isPageLoading" />
<slot></slot>
<component :is="currentComponent" v-if="!isPageLoading && currentComponent != null" />
<AdminPageLoading v-if="isPageLoading" />
</view>
<AdminFooter />
@@ -116,6 +117,13 @@ import type { TabItem } from '@/layouts/admin/store/adminNavStore.uts'
import { getComponent } from '@/layouts/admin/router/adminComponentMap.uts'
const props = defineProps({
currentPage: {
type: String,
default: ''
}
})
// 侧边栏宽度配置 (CRMEB 1:1)
const ASIDE_W = 70
const SUB_W = 200
@@ -374,6 +382,9 @@ let resizeTid: any = null
onMounted(() => {
initNavState()
if (props.currentPage != '') {
openRoute(props.currentPage as string)
}
// 初始化窗口宽度
windowWidth.value = uni.getWindowInfo().windowWidth

View File

@@ -1,24 +1,41 @@
<template>
<view class="admin-page">
<view class="admin-card header-card">
<view class="title-row">
<text class="title">版本管理</text>
<button class="btn primary">添加版本</button>
</view>
<view class="action-bar">
<button class="btn primary" @click="handlePublish">发布版本</button>
</view>
<view class="admin-card content-card">
<view class="admin-table">
<!-- 表头 -->
<view class="table-header">
<text class="cell">版本号</text>
<text class="cell">更新内容</text>
<text class="cell">强制更新</text>
<text class="cell">下载地址</text>
<text class="cell">创建时间</text>
<text class="cell actions">操作</text>
<view class="col col-version"><text>版本号</text></view>
<view class="col col-platform"><text>平台类型</text></view>
<view class="col col-info"><text>升级信息</text></view>
<view class="col col-force"><text>是否强制</text></view>
<view class="col col-date"><text>发布日期</text></view>
<view class="col col-url"><text>下载地址</text></view>
<view class="col col-ops"><text>操作</text></view>
</view>
<view class="table-empty">
<text>暂无版本数据</text>
<!-- 表格内容 -->
<view class="table-body">
<view v-if="versionList.length === 0" class="table-empty">
<text class="empty-txt">暂无数据</text>
</view>
<view v-for="item in versionList" :key="item.id" class="table-row">
<view class="col col-version"><text>{{ item.version }}</text></view>
<view class="col col-platform"><text>{{ item.platform }}</text></view>
<view class="col col-info"><text>{{ item.info }}</text></view>
<view class="col col-force"><text>{{ item.isForce ? '是' : '否' }}</text></view>
<view class="col col-date"><text>{{ item.date }}</text></view>
<view class="col col-url"><text class="link">{{ item.url }}</text></view>
<view class="col col-ops">
<text class="op-link" @click="handleEdit(item)">编辑</text>
<text class="op-split">|</text>
<text class="op-link delete" @click="handleDelete(item)">删除</text>
</view>
</view>
</view>
</view>
</view>
@@ -26,13 +43,137 @@
</template>
<script setup lang="uts">
// 版本管理逻辑
import { ref } from 'vue'
interface VersionItem {
id: number
version: string
platform: string
info: string
isForce: boolean
date: string
url: string
}
const versionList = ref<VersionItem[]>([])
const handlePublish = () => {
uni.showToast({ title: '暂不支持发布', icon: 'none' })
}
const handleEdit = (item: VersionItem) => {
console.log('Edit', item)
}
const handleDelete = (item: VersionItem) => {
uni.showModal({
title: '提示',
content: '确定要删除该版本吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '删除成功', icon: 'success' })
}
}
})
}
</script>
<style scoped>
<style scoped lang="scss">
.admin-page {
padding: 20px;
}
.action-bar {
margin-bottom: 20px;
}
.btn.primary {
width: 100px !important;
height: 36px !important;
line-height: 36px !important;
background: #1890ff !important;
color: #fff !important;
font-size: 14px !important;
border-radius: 4px !important;
text-align: center !important;
border: none;
}
.content-card {
background: #fff;
border-radius: 4px;
overflow: hidden;
}
.admin-table {
width: 100%;
}
.table-header {
display: flex;
flex-direction: row;
background: #fafafa;
border-bottom: 1px solid #f0f0f0;
}
.col {
padding: 12px 15px;
font-size: 14px;
color: #333;
display: flex;
align-items: center;
&.col-version { width: 100px; }
&.col-platform { width: 120px; }
&.col-info { flex: 1; }
&.col-force { width: 100px; }
&.col-date { width: 150px; }
&.col-url { width: 200px; }
&.col-ops { width: 120px; justify-content: center; }
}
.table-header .col {
font-weight: bold;
}
.table-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.table-empty {
padding: 100px;
padding: 100px 0;
text-align: center;
color: #909399;
width: 100%;
.empty-txt {
font-size: 14px;
color: #999;
}
}
.link {
color: #1890ff;
}
.op-link {
color: #1890ff;
font-size: 14px;
cursor: pointer;
&.delete {
color: #ff4d4f;
}
}
.op-split {
margin: 0 8px;
color: #e8e8e8;
}
</style>

View File

@@ -1,24 +1,94 @@
<template>
<view class="admin-page">
<view class="admin-card content-card">
<view class="tabs-row">
<view class="tab-item active">PC端配置</view>
<view class="tab-item active">
<text class="tab-text">PC站点配置</text>
</view>
</view>
<view class="config-form">
<view class="form-item">
<text class="label">网站域名</text>
<input class="form-input" placeholder="请输入PC端网站域名" />
<text class="tip">PC端访问的地址</text>
<view class="form-item row">
<text class="label">PC端LOGO</text>
<view class="form-content">
<view class="logo-upload" @click="handleUploadLogo">
<image v-if="config.logo" :src="config.logo" mode="aspectFit" class="logo-preview" />
<view v-else class="upload-placeholder">
<text class="plus">+</text>
</view>
</view>
<text class="tip">PC端LOGO</text>
</view>
</view>
<view class="form-item">
<text class="label">ICP备案号</text>
<input class="form-input" placeholder="请输入备案号" />
<text class="tip">展示在页面底部的备案信息</text>
<view class="form-item row">
<text class="label">联系电话:</text>
<view class="form-content">
<input class="form-input" v-model="config.phone" placeholder="400-8888-794" />
<text class="tip">PC底部显示的联系电话</text>
</view>
</view>
<view class="form-item row">
<text class="label">公司地址:</text>
<view class="form-content">
<input class="form-input" v-model="config.address" placeholder="陕西省西安市..." />
<text class="tip">PC底部显示的公司地址</text>
</view>
</view>
<view class="form-item row">
<text class="label">关键词:</text>
<view class="form-content">
<input class="form-input" v-model="config.keywords" placeholder="请输入关键词" />
<text class="tip">网站关键词</text>
</view>
</view>
<view class="form-item row">
<text class="label">网站描述:</text>
<view class="form-content">
<textarea class="form-textarea" v-model="config.description" placeholder="网站描述" />
<text class="tip">网站描述</text>
</view>
</view>
<view class="form-item row">
<text class="label">二维码类型:</text>
<view class="form-content">
<view class="radio-group">
<view class="radio-item" @click="config.qrCodeType = 'official'">
<view class="radio-circle" :class="{ active: config.qrCodeType === 'official' }"></view>
<text class="radio-text">公众号</text>
</view>
<view class="radio-item" @click="config.qrCodeType = 'routine'">
<view class="radio-circle" :class="{ active: config.qrCodeType === 'routine' }"></view>
<text class="radio-text">小程序</text>
</view>
</view>
<text class="tip">商品详情手机购买显示公众号或者小程序码</text>
</view>
</view>
<view class="form-item row">
<text class="label">精品推荐个数:</text>
<view class="form-content">
<input class="form-input" type="number" v-model="config.recommendCount" placeholder="3" />
<text class="tip">首页配置精品推荐个数</text>
</view>
</view>
<view class="form-item row">
<text class="label">首发新品个数:</text>
<view class="form-content">
<input class="form-input" type="number" v-model="config.newProductCount" placeholder="5" />
<text class="tip">首页配置首发新品个数</text>
</view>
</view>
<view class="form-btns">
<button class="btn primary">提交</button>
<button class="btn primary" @click="handleSubmit">提交</button>
</view>
</view>
</view>
@@ -26,41 +96,216 @@
</template>
<script setup lang="uts">
// PC配置逻辑
import { ref } from 'vue'
const config = ref({
logo: 'https://v5.crmeb.net/uploads/attach/2022/05/20220516/6198f7e6f8a8b.png',
phone: '400-8888-794',
address: '陕西省西安市西咸新区洋东新城能源金融贸易区金湾大厦3层',
keywords: '',
description: '',
qrCodeType: 'routine',
recommendCount: 3,
newProductCount: 5
})
const handleUploadLogo = () => {
uni.chooseImage({
count: 1,
success: (res) => {
config.value.logo = res.tempFilePaths[0]
}
})
}
const handleSubmit = () => {
uni.showToast({ title: '保存成功', icon: 'success' })
}
</script>
<style scoped>
.config-form {
padding: 40px 10%;
<style scoped lang="scss">
.admin-page {
padding: 20px;
}
.form-item {
margin-bottom: 30px;
display: flex;
flex-direction: column;
}
.form-item .label {
font-weight: bold;
margin-bottom: 10px;
color: #303133;
}
.form-input {
border: 1px solid #dcdfe6;
padding: 10px 15px;
.content-card {
background: #fff;
border-radius: 4px;
}
.tabs-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
padding: 0 20px;
}
.tab-item {
padding: 15px 20px;
position: relative;
&.active {
.tab-text {
color: #1890ff;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: #1890ff;
}
}
}
.tab-text {
font-size: 14px;
color: #333;
}
.config-form {
padding: 30px 20px;
max-width: 800px;
}
.form-item {
margin-bottom: 25px;
&.row {
display: flex;
flex-direction: row;
align-items: flex-start;
}
}
.label {
width: 140px;
text-align: right;
font-size: 14px;
color: #333;
padding-top: 8px;
margin-right: 15px;
}
.form-content {
flex: 1;
}
.logo-upload {
width: 80px;
height: 80px;
border: 1px dashed #dcdee2;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover {
border-color: #1890ff;
}
}
.logo-preview {
width: 100%;
height: 100%;
}
.upload-placeholder {
.plus {
font-size: 24px;
color: #ccc;
}
}
.form-input {
width: 100%;
max-width: 460px;
height: 36px;
border: 1px solid #dcdee2;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
}
.form-textarea {
width: 100%;
max-width: 460px;
height: 100px;
border: 1px solid #dcdee2;
border-radius: 4px;
padding: 8px 12px;
font-size: 14px;
}
.tip {
display: block;
font-size: 12px;
color: #909399;
color: #999;
margin-top: 8px;
}
.radio-group {
display: flex;
flex-direction: row;
align-items: center;
height: 36px;
}
.radio-item {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 25px;
cursor: pointer;
}
.radio-circle {
width: 16px;
height: 16px;
border: 1px solid #dcdee2;
border-radius: 50%;
margin-right: 6px;
position: relative;
&.active {
border-color: #1890ff;
&::after {
content: '';
width: 8px;
height: 8px;
background: #1890ff;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
.radio-text {
font-size: 14px;
color: #333;
}
.form-btns {
margin-top: 40px;
padding-left: 155px;
}
.btn.primary {
width: 80px;
background-color: #2d8cf0;
width: 64px;
height: 32px;
line-height: 32px;
background: #1890ff;
color: #fff;
font-size: 14px;
border-radius: 4px;
text-align: center;
border: none;
}
</style>

View File

@@ -1,24 +1,164 @@
<template>
<view class="admin-page">
<view class="admin-card content-card">
<!-- Tabs (1:1 CRMEB Style) -->
<view class="tabs-row">
<view class="tab-item active">小程序配置</view>
<view
class="tab-item"
:class="{ active: activeTab === 'config' }"
@click="activeTab = 'config'"
>
<text class="tab-text">小程序配置</text>
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'message' }"
@click="activeTab = 'message'"
>
<text class="tab-text">消息推送配置</text>
</view>
</view>
<view class="config-form">
<view class="form-item">
<text class="label">AppID</text>
<input class="form-input" placeholder="请输入小程序AppID" />
<text class="tip">微信小程序的AppID</text>
<!-- Form Area -->
<view class="config-content">
<!-- Tab 1: 小程序配置 (Image 1) -->
<view v-if="activeTab === 'config'" class="form-body">
<view class="form-item row">
<text class="label">小程序名称:</text>
<view class="form-content">
<input class="form-input" v-model="config.name" placeholder="请输入小程序名称" />
<text class="tip">小程序名称</text>
</view>
</view>
<view class="form-item row">
<text class="label">客服类型:</text>
<view class="form-content">
<view class="radio-group">
<view class="radio-item" @click="config.kefuType = 'system'">
<view class="radio-circle" :class="{ checked: config.kefuType === 'system' }"></view>
<text class="radio-text">跟随系统</text>
</view>
<view class="radio-item" @click="config.kefuType = 'routine'">
<view class="radio-circle" :class="{ checked: config.kefuType === 'routine' }"></view>
<text class="radio-text">小程序客服</text>
</view>
</view>
<text class="tip line-height-tip">跟随系统:跟随系统使用默认客服、电话或者跳转链接;小程序客服:需要在小程序后台配置客服用户;</text>
</view>
</view>
<view class="form-item row">
<text class="label">手机号获取方式:</text>
<view class="form-content">
<view class="checkbox-group">
<view class="checkbox-item" @click="config.authPhone = !config.authPhone">
<view class="checkbox-box" :class="{ checked: config.authPhone }">
<text v-if="config.authPhone" class="check-ic">v</text>
</view>
<text class="checkbox-text">微信授权</text>
</view>
<view class="checkbox-item" @click="config.manualPhone = !config.manualPhone">
<view class="checkbox-box" :class="{ checked: config.manualPhone }">
<text v-if="config.manualPhone" class="check-ic">v</text>
</view>
<text class="checkbox-text">手动填写</text>
</view>
</view>
<text class="tip">小程序获取手机号的方式,微信授权和手机号验证码</text>
</view>
</view>
<view class="form-item row">
<text class="label">强制获取昵称头像:</text>
<view class="form-content">
<view class="radio-group">
<view class="radio-item" @click="config.forceUserInfo = true">
<view class="radio-circle" :class="{ checked: config.forceUserInfo }"></view>
<text class="radio-text">开启</text>
</view>
<view class="radio-item" @click="config.forceUserInfo = false">
<view class="radio-circle" :class="{ checked: !config.forceUserInfo }"></view>
<text class="radio-text">关闭</text>
</view>
</view>
<text class="tip">是否在小程序用户授权之后,弹窗获取用户的昵称和头像</text>
</view>
</view>
<view class="form-item row">
<text class="label">发货信息管理:</text>
<view class="form-content">
<view class="radio-group">
<view class="radio-item" @click="config.deliveryInfo = true">
<view class="radio-circle" :class="{ checked: config.deliveryInfo }"></view>
<text class="radio-text">开启</text>
</view>
<view class="radio-item" @click="config.deliveryInfo = false">
<view class="radio-circle" :class="{ checked: !config.deliveryInfo }"></view>
<text class="radio-text">关闭</text>
</view>
</view>
<text class="tip line-height-tip">小程序有订单发货管理时,请打开此开关,否则会导致订单资金冻结</text>
</view>
</view>
<view class="form-btns">
<button class="btn primary" @click="handleSubmit">提交</button>
</view>
</view>
<view class="form-item">
<text class="label">AppSecret</text>
<input class="form-input" placeholder="请输入小程序AppSecret" />
<text class="tip">微信小程序的AppSecret</text>
</view>
<view class="form-btns">
<button class="btn primary">提交</button>
<!-- Tab 2: 消息推送配置 (Image 2) -->
<view v-if="activeTab === 'message'" class="form-body">
<view class="form-item row">
<text class="label">接口地址:</text>
<view class="form-content">
<input class="form-input readonly" v-model="messageConfig.apiUrl" readonly />
<text class="tip">配置小程序消息推送使用的接口地址,直接复制输入框内容(此项系统生成,无法修改)</text>
</view>
</view>
<view class="form-item row">
<text class="label">小程序验证TOKEN</text>
<view class="form-content">
<input class="form-input" v-model="messageConfig.token" placeholder="请输入小程序验证TOKEN" />
<text class="tip">小程序验证TOKEN</text>
</view>
</view>
<view class="form-item row">
<text class="label">消息加解密方式:</text>
<view class="form-content">
<view class="radio-group">
<view class="radio-item" @click="messageConfig.encryptMode = 'plain'">
<view class="radio-circle" :class="{ checked: messageConfig.encryptMode === 'plain' }"></view>
<text class="radio-text">明文模式</text>
</view>
<view class="radio-item" @click="messageConfig.encryptMode = 'compat'">
<view class="radio-circle" :class="{ checked: messageConfig.encryptMode === 'compat' }"></view>
<text class="radio-text">兼容模式</text>
</view>
<view class="radio-item" @click="messageConfig.encryptMode = 'safe'">
<view class="radio-circle" :class="{ checked: messageConfig.encryptMode === 'safe' }"></view>
<text class="radio-text">安全模式</text>
</view>
</view>
<text class="tip">小程序消息推送中的消息加密方式,暂时仅支持明文模式</text>
</view>
</view>
<view class="form-item row">
<text class="label">EncodingAESKey</text>
<view class="form-content">
<input class="form-input" v-model="messageConfig.aesKey" placeholder="请输入EncodingAESKey" />
<text class="tip">消息加密密钥由43位字符组成字符范围为A-Z,a-z,0-9</text>
</view>
</view>
<view class="form-btns">
<button class="btn primary" @click="handleSubmit">提交</button>
</view>
</view>
</view>
</view>
@@ -26,41 +166,214 @@
</template>
<script setup lang="uts">
// 小程序配置逻辑
import { ref } from 'vue'
const activeTab = ref('config')
const config = ref({
name: 'CRMEB标准版',
kefuType: 'system',
authPhone: true,
manualPhone: false,
forceUserInfo: true,
deliveryInfo: true
})
const messageConfig = ref({
apiUrl: 'https://v5.crmeb.net/api/wechat/miniServe',
token: '',
encryptMode: 'plain',
aesKey: ''
})
function handleSubmit() {
uni.showToast({
title: '保存成功',
icon: 'success'
})
}
</script>
<style scoped>
.config-form {
padding: 40px 10%;
<style scoped lang="scss">
.admin-page {
padding: 20px;
}
.form-item {
margin-bottom: 30px;
display: flex;
flex-direction: column;
}
.form-item .label {
font-weight: bold;
margin-bottom: 10px;
color: #303133;
}
.form-input {
border: 1px solid #dcdfe6;
padding: 10px 15px;
.content-card {
background: #fff;
border-radius: 4px;
}
.tabs-row {
display: flex;
flex-direction: row;
border-bottom: 1px solid #f0f0f0;
padding: 0 20px;
}
.tab-item {
padding: 16px 20px;
cursor: pointer;
position: relative;
&.active {
.tab-text {
color: #1890ff;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: #1890ff;
}
}
}
.tab-text {
font-size: 14px;
color: #666;
}
.config-content {
padding: 30px 20px;
}
.form-body {
max-width: 800px;
}
.form-item {
margin-bottom: 25px;
&.row {
display: flex;
flex-direction: row;
align-items: flex-start;
}
}
.label {
width: 140px;
text-align: right;
font-size: 14px;
color: #333;
padding-top: 8px;
margin-right: 15px;
}
.form-content {
flex: 1;
}
.form-input {
width: 100%;
max-width: 460px;
height: 34px;
border: 1px solid #dcdee2;
border-radius: 4px;
padding: 0 12px;
font-size: 14px;
&.readonly {
background-color: #f3f3f3;
}
}
.tip {
display: block;
font-size: 12px;
color: #909399;
color: #999;
margin-top: 8px;
&.line-height-tip {
line-height: 1.6;
max-width: 460px;
}
}
.radio-group, .checkbox-group {
display: flex;
flex-direction: row;
align-items: center;
height: 34px;
}
.radio-item, .checkbox-item {
display: flex;
flex-direction: row;
align-items: center;
margin-right: 25px;
cursor: pointer;
}
.radio-circle {
width: 14px;
height: 14px;
border: 1px solid #dcdee2;
border-radius: 50%;
margin-right: 6px;
position: relative;
&.checked {
border-color: #1890ff;
&::after {
content: '';
width: 8px;
height: 8px;
background: #1890ff;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
.checkbox-box {
width: 14px;
height: 14px;
border: 1px solid #dcdee2;
border-radius: 2px;
margin-right: 6px;
display: flex;
align-items: center;
justify-content: center;
&.checked {
background-color: #1890ff;
border-color: #1890ff;
}
}
.check-ic {
color: #fff;
font-size: 10px;
}
.radio-text, .checkbox-text {
font-size: 14px;
color: #333;
}
.form-btns {
margin-top: 40px;
padding-left: 155px;
}
.btn.primary {
width: 80px;
background-color: #2d8cf0;
width: 64px;
height: 32px;
line-height: 32px;
background: #1890ff;
color: #fff;
font-size: 14px;
border-radius: 4px;
text-align: center;
border: none;
}
</style>