up
@ -198,3 +198,32 @@ export function getApps(params: Record<string, any>) {
|
|||||||
export function copyDiy(params: Record<string, any>) {
|
export function copyDiy(params: Record<string, any>) {
|
||||||
return request.post(`diy/copy`, params, { showSuccessMessage: true })
|
return request.post(`diy/copy`, params, { showSuccessMessage: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************************** 主题风格 ****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认主题配色
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function getDefaultTheme(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/theme/color`, {params})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取自定义主题配色
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function getDiyTheme(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/theme`, {params})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置主题配色
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function setDiyTheme(params: Record<string, any>) {
|
||||||
|
return request.post(`diy/theme`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
228
admin/src/app/api/diy_form.ts
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/***************************************************** 万能表单 ****************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单分页列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getDiyFormPageList(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getDiyFormList(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/list`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单详情
|
||||||
|
* @param form_id 万能表单id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getDiyFormInfo(form_id: number) {
|
||||||
|
return request.get(`diy/form/${ form_id }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加万能表单
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function addDiyForm(params: Record<string, any>) {
|
||||||
|
return request.post('diy/form', params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑万能表单
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function editDiyForm(params: Record<string, any>) {
|
||||||
|
return request.put(`diy/form/${ params.form_id }`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改万能表单分享内容
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function editDiyFormShare(params: Record<string, any>) {
|
||||||
|
return request.put(`diy/form/share`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除万能表单
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function deleteDiyForm(params: Record<string, any>) {
|
||||||
|
return request.put(`diy/form/delete`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单初始化数据
|
||||||
|
*/
|
||||||
|
export function initPage(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/init`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单微信小程序二维码
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getDiyFormQrcode(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/qrcode`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单字段列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getDiyFormFieldsList(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/fields/list`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段统计列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getDiyFormFieldStat(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/records/field/stat`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取页面模板类型
|
||||||
|
*/
|
||||||
|
export function getDiyTemplate(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/template`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模板页面列表
|
||||||
|
*/
|
||||||
|
export function getDiyTemplatePages(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/template`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 万能表单状态状态
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function editFormStatus(params: Record<string, any>) {
|
||||||
|
return request.put(`diy/form/status`, params, {
|
||||||
|
showErrorMessage: true,
|
||||||
|
showSuccessMessage: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模板页面(存在的应用插件列表)
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getApps(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/apps`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制模版页面
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function copyDiy(params: Record<string, any>) {
|
||||||
|
return request.post(`diy/form/copy`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单类型
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getFormType(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/type`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单填写配置
|
||||||
|
* @param form_id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getFormWriteConfig(form_id: any) {
|
||||||
|
return request.get(`diy/form/write/${ form_id }`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑万能表单填写配置
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function editDiyFormWriteConfig(params: Record<string, any>) {
|
||||||
|
return request.put(`diy/form/write`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单提交成功页配置
|
||||||
|
* @param form_id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getFormSubmitConfig(form_id: any) {
|
||||||
|
return request.get(`diy/form/submit/${ form_id }`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑万能表单提交成功页配置
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function editDiyFormSubmitConfig(params: Record<string, any>) {
|
||||||
|
return request.put(`diy/form/submit`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单数据列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getFormRecords(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/records`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单数据详情
|
||||||
|
* @param id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getFormRecordsInfo(id: number) {
|
||||||
|
return request.get(`diy/form/records/${ id }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除万能表单数据
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function deleteFormRecords(params: Record<string, any>) {
|
||||||
|
return request.put(`diy/form/records/delete`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取万能表单填表人列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getFormRecordsMember(params: Record<string, any>) {
|
||||||
|
return request.get(`diy/form/records/member/stat`, { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制模版页面
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function copyForm(params: Record<string, any>) {
|
||||||
|
return request.post(`diy/form/copy`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
@ -368,6 +368,21 @@ export function memberTransfer(params: Record<string, any>) {
|
|||||||
return request.put(`member/cash_out/transfer/${params.id}`, params, { showSuccessMessage: true })
|
return request.put(`member/cash_out/transfer/${params.id}`, params, { showSuccessMessage: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会员提现转账
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function memberRemark(params: Record<string, any>) {
|
||||||
|
return request.put(`member/cash_out/remark/${params.id}`, params, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查打款进度
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function memberCheck(id: number) {
|
||||||
|
return request.put(`member/cash_out/check/${id}`, {}, { showSuccessMessage: true })
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 会员状态变更
|
* 会员状态变更
|
||||||
* @param params
|
* @param params
|
||||||
|
|||||||
@ -91,10 +91,11 @@ export function pay(params: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 帮付
|
* 帮付
|
||||||
* @param params
|
* @param tradeType
|
||||||
* @returns
|
* @param tradeId
|
||||||
*/
|
* @param channel
|
||||||
|
*/
|
||||||
export function getFriendsPay(tradeType : string, tradeId : number, channel: string) {
|
export function getFriendsPay(tradeType : string, tradeId : number, channel: string) {
|
||||||
return request.get(`pay/friendspay/info/${tradeType}/${tradeId}/${channel}`, { showErrorMessage: false })
|
return request.get(`pay/friendspay/info/${tradeType}/${tradeId}/${channel}`, { showErrorMessage: false })
|
||||||
}
|
}
|
||||||
@ -254,3 +254,11 @@ export function getAccountType() {
|
|||||||
export function getSiteAddons() {
|
export function getSiteAddons() {
|
||||||
return request.get('site/addons')
|
return request.get('site/addons')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取站点应用
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getShowApp() {
|
||||||
|
return request.get('site/showApp')
|
||||||
|
}
|
||||||
|
|||||||
@ -410,7 +410,7 @@ export function getTransferInfo(channel: string) {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function setTransferInfo(params: Record<string, any>) {
|
export function setTransferInfo(params: Record<string, any>) {
|
||||||
return request.post(`pay/channel/set/transfer`, params)
|
return request.post(`pay/channel/set/transfer`, params, { showSuccessMessage: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************** 定时任务 ****************************************************/
|
/***************************************************** 定时任务 ****************************************************/
|
||||||
|
|||||||
BIN
admin/src/app/assets/images/diy_form/mobile_bottom.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
admin/src/app/assets/images/diy_form/mobile_line.png
Normal file
|
After Width: | Height: | Size: 167 B |
BIN
admin/src/app/assets/images/diy_form/mobile_tabbar.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 30 KiB |
BIN
admin/src/app/assets/images/index/app_store.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
admin/src/app/assets/images/index/install.png
Normal file
|
After Width: | Height: | Size: 838 B |
BIN
admin/src/app/assets/images/index/not_install.png
Normal file
|
After Width: | Height: | Size: 964 B |
BIN
admin/src/app/assets/images/index/site2.png
Normal file
|
After Width: | Height: | Size: 880 B |
BIN
admin/src/app/assets/images/index/site3.png
Normal file
|
After Width: | Height: | Size: 1011 B |
BIN
admin/src/app/assets/images/index/site_add.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
admin/src/app/assets/images/index/site_list.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
admin/src/app/assets/images/index/site_normal.png
Normal file
|
After Width: | Height: | Size: 957 B |
BIN
admin/src/app/assets/images/index/site_tc.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
admin/src/app/assets/images/index/site_user.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
admin/src/app/assets/images/login/login_bg.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
admin/src/app/assets/images/login/login_icon.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
admin/src/app/assets/images/login/password.png
Normal file
|
After Width: | Height: | Size: 466 B |
BIN
admin/src/app/assets/images/login/username.png
Normal file
|
After Width: | Height: | Size: 602 B |
BIN
admin/src/app/assets/images/logo.default.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 24 KiB |
BIN
admin/src/app/assets/images/site_default.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 33 KiB |
@ -10,7 +10,7 @@
|
|||||||
<div class="mt-[10px]" v-if="upgradeContent.upgrade_version != upgradeContent.last_version">
|
<div class="mt-[10px]" v-if="upgradeContent.upgrade_version != upgradeContent.last_version">
|
||||||
<el-alert type="info" show-icon>
|
<el-alert type="info" show-icon>
|
||||||
<template #title>
|
<template #title>
|
||||||
当前最新版本为{{ upgradeContent.last_version }},您的服务已于{{ upgradeContent.expire_time }}到期。如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级
|
当前最新版本为{{ upgradeContent.last_version }},您的服务{{ upgradeContent.expire_time ? `已于${upgradeContent.expire_time}到期` : '长期有效' }}。如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级
|
||||||
</template>
|
</template>
|
||||||
</el-alert>
|
</el-alert>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
295
admin/src/app/lang/zh-cn/diy_form.edit.json
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
{
|
||||||
|
"templatePagePlaceholder": "选择模板",
|
||||||
|
"templatePageEmpty": "无",
|
||||||
|
"changeTemplatePageTips": "切换模板后,当前页面内容将被替换且不被保存,请谨慎操作",
|
||||||
|
"developTitle": "开发环境配置",
|
||||||
|
"wapDomain": "wap域名(WAP_DOMAIN)",
|
||||||
|
"wapDomainPlaceholder": "请输入wap域名",
|
||||||
|
"pageSet": "页面设置",
|
||||||
|
"tabEditContent": "内容",
|
||||||
|
"tabEditStyle": "样式",
|
||||||
|
"pageStyle": "页面样式",
|
||||||
|
"pageContent": "页面内容",
|
||||||
|
"statusBarContent": "导航栏内容",
|
||||||
|
"statusBarStyle": "导航栏样式",
|
||||||
|
"statusBarSwitchTips": "此处控制当前页面导航栏是否显示",
|
||||||
|
"bottomNavContent": "底部导航内容",
|
||||||
|
"diyPageTitle": "页面名称",
|
||||||
|
"diyPageTitlePlaceholder": "请输入页面名称",
|
||||||
|
"pageTitleTips": "页面名称用于后台显示",
|
||||||
|
"diyTitle": "页面标题",
|
||||||
|
"diyTitlePlaceholder": "请输入页面标题",
|
||||||
|
"titleTips": "页面标题用于前台显示",
|
||||||
|
"pageBgColor": "页面颜色",
|
||||||
|
"bgUrl": "背景图片",
|
||||||
|
"bgHeightScale": "高度比例",
|
||||||
|
"bgHeightScaleTip": "为0时背景高度自适应展示",
|
||||||
|
"marginSet": "边距设置",
|
||||||
|
"componentStyleTitle": "组件样式",
|
||||||
|
"bottomBgColor": "底部背景",
|
||||||
|
"bottomBgTips": "底部背景包含边距和圆角",
|
||||||
|
"componentBgColor": "组件背景色",
|
||||||
|
"componentBgUrl": "组件背景图",
|
||||||
|
"componentBgAlpha": "透明度",
|
||||||
|
"bgGradientAngle": "渐变角度",
|
||||||
|
"topToBottom": "从上到下",
|
||||||
|
"leftToRight": "从左到右",
|
||||||
|
"marginTop": "上边距",
|
||||||
|
"marginBottom": "下边距",
|
||||||
|
"marginBoth": "左右边距",
|
||||||
|
"topRounded": "上圆角",
|
||||||
|
"bottomRounded": "下圆角",
|
||||||
|
"warmPrompt": "温馨提示",
|
||||||
|
"leavePageTitleTips": "确定离开此页面?",
|
||||||
|
"leavePageContentTips": "系统可能不会保存您所做的更改。",
|
||||||
|
"decorating": "正在装修",
|
||||||
|
"preview": "保存并预览",
|
||||||
|
"moveUpComponent": "上移",
|
||||||
|
"moveDownComponent": "下移",
|
||||||
|
"copyComponent": "复制",
|
||||||
|
"delComponent": "删除",
|
||||||
|
"resetComponent": "重置",
|
||||||
|
"tabbar": "底部导航",
|
||||||
|
"tabbarSwitchTips": "此处控制当前页面底部导航菜单是否显示",
|
||||||
|
"link": "链接地址",
|
||||||
|
"delComponentTips": "确认要删除当前组件吗?",
|
||||||
|
"notCopy": "无法复制",
|
||||||
|
"componentCanOnlyAdd": "组件只能添加",
|
||||||
|
"piece": "个",
|
||||||
|
"componentNotMoved": "该组件禁止移动",
|
||||||
|
"resetComponentTips": "确认要重置组件默认数据吗?",
|
||||||
|
"image": "图片上传",
|
||||||
|
"imageUpload": "图片上传",
|
||||||
|
"imageSet": "图片设置",
|
||||||
|
"imageAdsTips": "建议上传尺寸相同的图片,推荐尺寸750*350",
|
||||||
|
"imageAdsSameScreenTips": "开启沉浸式样式,请确保该图片广告组件在页面中位于最顶端;为保证体验,请不要开导航栏;沉浸式样式仅在微信小程序中生效。",
|
||||||
|
"sameScreen": "沉浸式",
|
||||||
|
"addImageAd": "添加图片",
|
||||||
|
"imageUrlTip": "请上传图片",
|
||||||
|
"imageHeight": "图片高度",
|
||||||
|
"imageHeightPlaceholder": "请输入图片高度",
|
||||||
|
"imageHeightRegNum": "图片高度格式错误,请输入数字",
|
||||||
|
"dataSources": "数据来源",
|
||||||
|
"defaultSources": "默认",
|
||||||
|
"manualSelectionSources": "手动选择",
|
||||||
|
"selectPlaceholder": "请选择",
|
||||||
|
"selected": "已选",
|
||||||
|
"graphicNavModeTitle": "导航模式",
|
||||||
|
"layoutMode": "排版模式",
|
||||||
|
"layoutModeHorizontal": "横排",
|
||||||
|
"layoutModeVertical": "竖排",
|
||||||
|
"graphicNavSelectMode": "选择模式",
|
||||||
|
"graphicNavModeGraphic": "图文导航",
|
||||||
|
"graphicNavModeImg": "图片导航",
|
||||||
|
"graphicNavModeText": "文字导航",
|
||||||
|
"graphicNavImageSet": "图片设置",
|
||||||
|
"graphicNavImageSize": "图片大小",
|
||||||
|
"graphicNavAroundRadius": "图片圆角",
|
||||||
|
"graphicNavShowStyle": "展示风格",
|
||||||
|
"graphicNavStyleFixed": "固定显示",
|
||||||
|
"graphicNavStyleSingleSlide": "单行滑动",
|
||||||
|
"graphicNavStyleMultiLine": "多行滑动",
|
||||||
|
"graphicNavStylePageSlide": "分页滑动",
|
||||||
|
"graphicNavRowCount": "每行数量",
|
||||||
|
"graphicNavPageCount": "显示方式",
|
||||||
|
"graphicNavSetLabel": "导航设置",
|
||||||
|
"singleLine": "单行",
|
||||||
|
"multiline": "多行",
|
||||||
|
"graphicNavTips": "建议上传尺寸相同的图片,推荐尺寸60*60",
|
||||||
|
"graphicNavTitle": "标题",
|
||||||
|
"graphicNavTitlePlaceholder": "请输入标题",
|
||||||
|
"subGraphicNavTitle": "副标题",
|
||||||
|
"subGraphicNavTitlePlaceholder": "请输入副标题",
|
||||||
|
"subGraphicNavTitleLink": "副标题链接",
|
||||||
|
"addGraphicNav": "添加导航",
|
||||||
|
"blankHeightSet": "高度设置",
|
||||||
|
"blankHeight": "空白高度",
|
||||||
|
"styleSet": "风格设置",
|
||||||
|
"titleStyle": "标题样式",
|
||||||
|
"selectStyle": "风格选择",
|
||||||
|
"activeCubeBlockBtnText": "按钮文字",
|
||||||
|
"btnTextItalics": "斜体",
|
||||||
|
"btnTextNormal": "常规",
|
||||||
|
"styleLabel": "风格",
|
||||||
|
"styleShowTips": "风格 1 2 3,仅在小程序中展示",
|
||||||
|
"titleContent": "标题内容",
|
||||||
|
"title": "标题名称",
|
||||||
|
"titlePlaceholder": "请输入标题",
|
||||||
|
"textAlign": "对齐方式",
|
||||||
|
"textAlignLeft": "居左",
|
||||||
|
"textAlignCenter": "居中",
|
||||||
|
"textAlignRight": "居右",
|
||||||
|
"textSet": "文字设置",
|
||||||
|
"textFontSize": "文字大小",
|
||||||
|
"textFontWeight": "文字粗细",
|
||||||
|
"fontWeightBold": "加粗",
|
||||||
|
"fontWeightNormal": "常规",
|
||||||
|
"textColor": "文字颜色",
|
||||||
|
"subTitleStyle": "副标题样式",
|
||||||
|
"subTextBgColor": "背景色",
|
||||||
|
"subTitleContent": "标题内容",
|
||||||
|
"subTitle": "副标题",
|
||||||
|
"subTitlePlaceholder": "请输入副标题",
|
||||||
|
"moreContent": "“更多”按钮内容",
|
||||||
|
"more": "文字",
|
||||||
|
"morePlaceholder": "请输入文字",
|
||||||
|
"moreIsShow": "是否显示",
|
||||||
|
"memberStyle": "会员样式",
|
||||||
|
"template": "模板",
|
||||||
|
"imageGap": "图片间隙",
|
||||||
|
"rubikCubeStyle": "魔方样式",
|
||||||
|
"rubikCubeLayout": "魔方布局",
|
||||||
|
"hotArea": "热区",
|
||||||
|
"hotAreaSet": "热区设置",
|
||||||
|
"hotAreaBackground": "热区背景",
|
||||||
|
"addHotArea": "添加热区",
|
||||||
|
"clickSet": "点击设置",
|
||||||
|
"selectedAfterHotArea": "个热区",
|
||||||
|
"hotAreaManage": "热区管理",
|
||||||
|
"selectedHotArea": "请选择热区",
|
||||||
|
"hotAreaLink": "的链接地址",
|
||||||
|
"addonListSet": "应用设置",
|
||||||
|
"addonListTips": "应用选择",
|
||||||
|
"selectAddonTips": "请选择应用",
|
||||||
|
"addonTitle": "应用名称",
|
||||||
|
"addonDesc": "应用描述",
|
||||||
|
"addonIcon": "应用图标",
|
||||||
|
"selectAddon": "选择应用",
|
||||||
|
"addAddon": "添加应用",
|
||||||
|
"show": "显示",
|
||||||
|
"hidden": "隐藏",
|
||||||
|
"goodsCategoryTitle": "商品分类",
|
||||||
|
"customGoods": "手动选择",
|
||||||
|
"goodsNum": "商品数量",
|
||||||
|
"selectCategory": "选择分类",
|
||||||
|
"categoryName": "分类名称",
|
||||||
|
"categoryImage": "分类图片",
|
||||||
|
"selectSource": "选择数据源",
|
||||||
|
"richTextContentSet": "内容设置",
|
||||||
|
"richTextPlaceholder": "请输入富文本内容",
|
||||||
|
"activeCubeBlockContent": "板块内容",
|
||||||
|
"activeCubeTitle": "标题",
|
||||||
|
"activeCubeTitlePlaceholder": "请输入标题",
|
||||||
|
"activeCubeSubTitle": "副标题",
|
||||||
|
"activeCubeSubTitlePlaceholder": "请输入副标题",
|
||||||
|
"activeCubeButton": "按钮",
|
||||||
|
"activeCubeButtonPlaceholder": "请输入按钮文字",
|
||||||
|
"activeCubeButtonColor": "按钮颜色",
|
||||||
|
"activeListFrameColor": "框体颜色",
|
||||||
|
"activeCubeSubTitleTextColor": "文字颜色",
|
||||||
|
"activeCubeSubTitleBgColor": "背景颜色",
|
||||||
|
"activeCubeAddItem": "添加一个板块",
|
||||||
|
"activeCubeBlockStyle": "板块样式",
|
||||||
|
"activeCubeBlockTextFontWeight": "标题粗细",
|
||||||
|
"noticeStyle": "公告风格",
|
||||||
|
"noticeType": "类型",
|
||||||
|
"noticeTypeImg": "图片",
|
||||||
|
"noticeTypeText": "文字",
|
||||||
|
"noticeTypeTextPlaceholder": "请输入公告标题",
|
||||||
|
"noticeTitle": "公告标题",
|
||||||
|
"addNotice": "添加公告",
|
||||||
|
"noticeText": "公告内容",
|
||||||
|
"noticeScrollWay": "滚动方式",
|
||||||
|
"noticeUpDown": "上下滚动",
|
||||||
|
"noticeHorizontal": "横向滚动",
|
||||||
|
"noticeShowType": "点击类型",
|
||||||
|
"noticeShowPopUp": "弹出公告内容",
|
||||||
|
"noticeShowLink": "跳转链接",
|
||||||
|
"dragMouseAdjustOrder": "鼠标拖拽可调整顺序",
|
||||||
|
"noticePlaceholderText": "请输入公告内容",
|
||||||
|
"carouselSearchShowPosition": "显示设置",
|
||||||
|
"carouselSearchOpen": "开启",
|
||||||
|
"carouselSearchClose": "关闭",
|
||||||
|
"carouselSearchBgGradient": "背景渐变",
|
||||||
|
"carouselSearchShowWay": "展示方式",
|
||||||
|
"carouselSearchShowWayStatic": "正常显示",
|
||||||
|
"carouselSearchShowWayFixed": "滚动至顶部固定",
|
||||||
|
"carouselSearchFixedBgColor": "置顶背景",
|
||||||
|
"carouselSearchStyleSelect": "风格选择",
|
||||||
|
"carouselSearchSet": "搜索设置",
|
||||||
|
"carouselSearchSubTitle": "副标题",
|
||||||
|
"carouselSearchSubTitleStyle": "副标题样式",
|
||||||
|
"carouselSearchPositionStyle": "定位样式",
|
||||||
|
"carouselSearchSubTitlePlaceholder": "请输入副标题内容",
|
||||||
|
"carouselSearchText": "搜索内容",
|
||||||
|
"carouselSearchTextColor": "文字颜色",
|
||||||
|
"carouselSearchBgColor": "背景颜色",
|
||||||
|
"carouselSearchBtnColor": "按钮颜色",
|
||||||
|
"carouselSearchBtnBgColor": "按钮背景色",
|
||||||
|
"carouselSearchHotWordSet": "搜索热词",
|
||||||
|
"carouselSearchHotWordInterval": "显示时间 / 秒",
|
||||||
|
"carouselSearchHotWordText": "内容",
|
||||||
|
"carouselSearchHotWordTextPlaceholder": "请输入热词",
|
||||||
|
"carouselSearchAddHotWordItem": "添加一个热词",
|
||||||
|
"carouselSearchLogoTips": "建议尺寸,70px * 30px",
|
||||||
|
"carouselSearchTextTips": "搜索内容是默认展示数据,当添加搜索热词时,搜索内容隐藏; 当没有搜索热词时,搜索内容展示",
|
||||||
|
"carouselSearchPlaceholder": "请输入搜索内容",
|
||||||
|
"carouselSearchTabSet": "选项卡设置",
|
||||||
|
"carouselSearchTabControl": "展示开关",
|
||||||
|
"carouselSearchTabCategoryText": "分类名称",
|
||||||
|
"carouselSearchTabCategoryTextPlaceholder": "请输入分类名称",
|
||||||
|
"carouselSearchAddTabItem": "添加一个选项卡",
|
||||||
|
"selectSourcesDiyPage": "选择微页面",
|
||||||
|
"selectDiyPagePlaceholder": "请选择微页面",
|
||||||
|
"diyPageTypeName": "页面类型",
|
||||||
|
"diyPageForAddon": "所属应用",
|
||||||
|
"carouselSearchSwiperSet": "轮播图设置",
|
||||||
|
"carouselSearchSwiperControl": "展示开关",
|
||||||
|
"carouselSearchSwiperInterval": "切换间隔 / 秒",
|
||||||
|
"carouselSearchSwiperTips": "建议上传尺寸相同的图片,推荐尺寸750*350;鼠标拖拽可调整图片顺序",
|
||||||
|
"carouselSearchTabStyle": "选项卡样式",
|
||||||
|
"carouselSearchStyle": "搜索框样式",
|
||||||
|
"noColor": "常规颜色",
|
||||||
|
"selectColor": "选中颜色",
|
||||||
|
"fixedNoColor": "下滑常规颜色",
|
||||||
|
"fixedSelectColor": "下滑选中颜色",
|
||||||
|
"carouselSearchSwiperIndicatorSet": "指示器设置",
|
||||||
|
"carouselSearchSwiperIndicatorStyle": "指示器样式",
|
||||||
|
"carouselSearchSwiperStyle": "轮播样式",
|
||||||
|
"carouselSearchSwiperIndicatorStyle1": "样式1",
|
||||||
|
"carouselSearchSwiperIndicatorStyle2": "样式2",
|
||||||
|
"carouselSearchSwiperIndicatorStyle3": "样式3",
|
||||||
|
"carouselSearchSwiperIndicatorAlign": "显示位置",
|
||||||
|
"alignLeft": "居左",
|
||||||
|
"alignCenter": "居中",
|
||||||
|
"alignRight": "居右",
|
||||||
|
"horzLineStyle": "线条风格",
|
||||||
|
"horzLineStyleSolid": "实线",
|
||||||
|
"horzLineStyleDashed": "虚线",
|
||||||
|
"horzLineBorderColor": "线条颜色",
|
||||||
|
"horzLineBorderWidth": "线条宽度",
|
||||||
|
"floatBtnBtton": "按钮位置",
|
||||||
|
"floatBtnOffset": "上下偏移",
|
||||||
|
"floatBtnImageSet": "图片设置",
|
||||||
|
"floatBtnImageSize": "图片大小",
|
||||||
|
"floatBtnAroundRadius": "图片圆角",
|
||||||
|
"floatBtnImageSuggest": "建议上传正方形图片",
|
||||||
|
"topStatusBarImg": "图片",
|
||||||
|
"topStatusBarNav": "导航栏",
|
||||||
|
"topStatusBarNavTips": "此处控制当前页面导航栏是否显示",
|
||||||
|
"topStatusBarImgTips": "宽度自适应(最大150px),高度28px",
|
||||||
|
"topStatusBarTextColor": "标题颜色",
|
||||||
|
"topStatusBarBgColor": "头部颜色",
|
||||||
|
"rollTopStatusBarBgColor": "滚动后头部颜色",
|
||||||
|
"rollTopStatusBarTextColor": "滚动后标题颜色",
|
||||||
|
"topStatusBarSearchName": "搜索内容",
|
||||||
|
"topStatusBarSearchNamePlaceholder": "请输入搜索关键词",
|
||||||
|
"settingTips": "点击查看如何配置",
|
||||||
|
"pictureShowBlockOne": "模块一",
|
||||||
|
"pictureShowBlockTwo": "模块二",
|
||||||
|
"subTitleTextColor": "标题颜色",
|
||||||
|
"pictureShowBgColor": "背景颜色",
|
||||||
|
"pictureShowBtnText": "按钮文字",
|
||||||
|
"pictureShowBtnColor": "文字颜色",
|
||||||
|
"pictureShowBtnBgColor": "背景颜色",
|
||||||
|
"pictureShowBlockStyle": "模块样式",
|
||||||
|
|
||||||
|
"fieldNamePlaceholder": "请输入字段名称",
|
||||||
|
"fieldRemarkPlaceholder": "请输入字段说明",
|
||||||
|
"defaultValue": "默认值",
|
||||||
|
"defaultValuePlaceholder": "请输入默认值",
|
||||||
|
"formPlaceholder": "提示语",
|
||||||
|
"formPlaceholderTips": "请输入提示语",
|
||||||
|
"isRequired": "是否必填",
|
||||||
|
"optionPlaceholder": "请输入选项内容"
|
||||||
|
}
|
||||||
49
admin/src/app/lang/zh-cn/diy_form.list.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"title": "表单名称",
|
||||||
|
"typeName": "表单类型",
|
||||||
|
"forAddon": "所属应用",
|
||||||
|
"forAddonPlaceholder": "请选择所属应用",
|
||||||
|
"addFormTips": "创建新表单",
|
||||||
|
"formTypePlaceholder": "请选择表单类型",
|
||||||
|
"nameMax": "名称不能超过12个字符",
|
||||||
|
"status": "状态",
|
||||||
|
"updateTime": "更新时间",
|
||||||
|
"statusOn": "启用",
|
||||||
|
"statusOff": "禁用",
|
||||||
|
"all": "全部",
|
||||||
|
"wapUrl": "wap链接",
|
||||||
|
"weappUrl": "小程序链接",
|
||||||
|
"shareLink": "分享链接",
|
||||||
|
"copy": "复制",
|
||||||
|
"copySuccess": "复制成功",
|
||||||
|
"titlePlaceholder": "请输入表单名称",
|
||||||
|
"addDiyForm": "添加表单",
|
||||||
|
"diyFormDeleteTips": "确定要删除该表单吗?",
|
||||||
|
"diyFormCopyTips": "确定要复制该表单吗?",
|
||||||
|
"preview": "预览",
|
||||||
|
"share": "分享",
|
||||||
|
"shareSet": "分享设置",
|
||||||
|
"sharePage": "分享表单",
|
||||||
|
"wechat": "微信公众号",
|
||||||
|
"weapp": "微信小程序",
|
||||||
|
"shareTitle": "分享标题",
|
||||||
|
"shareTitlePlaceholder": "请输入分享标题",
|
||||||
|
"shareDesc": "分享描述",
|
||||||
|
"shareDescPlaceholder": "请输入分享描述",
|
||||||
|
"shareImageUrl": "分享图片",
|
||||||
|
|
||||||
|
"joinMemberType": "参与会员",
|
||||||
|
"allMember": "所有会员参与",
|
||||||
|
"selectedMemberLevel": "指定会员等级",
|
||||||
|
"selectedMemberLabel": "指定会员标签",
|
||||||
|
"memberLevel": "会员等级",
|
||||||
|
"memberLevelPlaceholder": "请选择会员等级",
|
||||||
|
"memberLabel": "会员标签",
|
||||||
|
"memberLabelPlaceholder": "请选择会员标签",
|
||||||
|
"labelTips": "请选择会员标签",
|
||||||
|
"levelTips": "请选择会员等级",
|
||||||
|
|
||||||
|
"batchDeletion": "批量删除",
|
||||||
|
"batchEmptySelectedFormsTips": "请选择要删除的表单",
|
||||||
|
"batchFormsDeleteTips": "确定要删除选中的表单吗?"
|
||||||
|
}
|
||||||
@ -46,5 +46,19 @@
|
|||||||
"cashOutNumberPlaceholder": "请输入提现单号",
|
"cashOutNumberPlaceholder": "请输入提现单号",
|
||||||
"alipayAccount": "支付宝账号",
|
"alipayAccount": "支付宝账号",
|
||||||
"bankName": "银行名称",
|
"bankName": "银行名称",
|
||||||
"bankAccount": "银行卡号"
|
"bankAccount": "银行卡号",
|
||||||
|
"cashOutInfo":"收款方信息",
|
||||||
|
"transferCode":"收款码",
|
||||||
|
"realname":"真实姓名",
|
||||||
|
"account":"账号",
|
||||||
|
"bankRealname":"持卡人姓名",
|
||||||
|
"remark":"备注",
|
||||||
|
"remarkPlaceholder":"请输入备注",
|
||||||
|
"passAudit":"通过审核",
|
||||||
|
"transferVoucher":"转账凭证",
|
||||||
|
"transferVoucherPlaceholder":"请上传转账凭证",
|
||||||
|
"transferRemark":"转账补充说明",
|
||||||
|
"transferRemarkPlaceholder":"请输入转账补充说明",
|
||||||
|
"notes":"备注",
|
||||||
|
"check":"检查打款进度"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
"signPeriod": "签到周期",
|
"signPeriod": "签到周期",
|
||||||
"signPeriodTip": "请输入签到周期",
|
"signPeriodTip": "请输入签到周期",
|
||||||
"signPeriodLimitTips": "签到周期格式错误",
|
"signPeriodLimitTips": "签到周期格式错误",
|
||||||
"signPeriodMustZeroTips": "签到周期必须大于0",
|
"signPeriodMustZeroTips": "签到周期为2-365天",
|
||||||
"calendarSign": "日历签到",
|
"calendarSign": "日历签到",
|
||||||
"periodSign": "周期签到",
|
"periodSign": "周期签到",
|
||||||
"daySignAward": "日签奖励",
|
"daySignAward": "日签奖励",
|
||||||
@ -26,12 +26,16 @@
|
|||||||
"ruleExplainTip": "请输入规则说明",
|
"ruleExplainTip": "请输入规则说明",
|
||||||
"ruleExplainDefault": "1.每日签到可以获得日签奖励,连续签到可以获得连签奖励;\n2.每日最多可签到1次,断签则会重新计算连签天数;\n3.活动以及奖励最终解释权归商家所有。",
|
"ruleExplainDefault": "1.每日签到可以获得日签奖励,连续签到可以获得连签奖励;\n2.每日最多可签到1次,断签则会重新计算连签天数;\n3.活动以及奖励最终解释权归商家所有。",
|
||||||
"useDefaultExplain": "使用默认说明",
|
"useDefaultExplain": "使用默认说明",
|
||||||
"continueSign": "连续签到天数",
|
"continueSign": "连签天数",
|
||||||
|
"continueSignFormatError": "连签天数格式错误",
|
||||||
|
"continueSignBerweenDays": "连签天数为2-365天",
|
||||||
"receiveLimit": "领取限制",
|
"receiveLimit": "领取限制",
|
||||||
"noLimit": "不限制",
|
"noLimit": "不限制",
|
||||||
"everyOneLimit": "每人限领",
|
"everyOneLimit": "每人限领",
|
||||||
"time": "次",
|
"time": "次",
|
||||||
"day": "天",
|
"day": "天",
|
||||||
"continueSignPlaceholder":"请输入连续签到天数",
|
"continueSignPlaceholder":"请输入连签天数",
|
||||||
"receiveNumPlaceholder":"请输入限领次数"
|
"receiveNumPlaceholder":"请输入限领次数",
|
||||||
|
"receiveNumFormatError":"限领次数格式错误",
|
||||||
|
"receiveNumMustGreaterThanZeroTip":"限领次数不能小于等于0"
|
||||||
}
|
}
|
||||||
@ -3,6 +3,6 @@
|
|||||||
"type": "协议类型",
|
"type": "协议类型",
|
||||||
"titlePlaceholder": "请输入协议标题",
|
"titlePlaceholder": "请输入协议标题",
|
||||||
"contentPlaceholder": "请填写协议内容",
|
"contentPlaceholder": "请填写协议内容",
|
||||||
"contentMaxTips": "协议内容字符数应在5~50000之间",
|
"contentMaxTips": "协议内容字符数应在5~100000之间",
|
||||||
"content": "内容"
|
"content": "内容"
|
||||||
}
|
}
|
||||||
@ -14,5 +14,8 @@
|
|||||||
"automatedTransit": "自动转账",
|
"automatedTransit": "自动转账",
|
||||||
"manualTransfer": "手动转账",
|
"manualTransfer": "手动转账",
|
||||||
"wechat": "微信",
|
"wechat": "微信",
|
||||||
"alipay": "支付宝"
|
"alipay": "支付宝",
|
||||||
|
"minTips":"注意:微信零钱最低提现金额为0.1",
|
||||||
|
"transferTips":"只有微信零钱支持自动转账,微信零钱可能会遇到资金不足、超过当日转账上限等因素的情况下会导致转账失败,停留在待转账状态下,需要管理员手动在后台操作",
|
||||||
|
"transferModeTips":"仅有微信零钱这一种转账方式支持线上打款,其余转账方式皆只支持线下打款"
|
||||||
}
|
}
|
||||||
@ -81,5 +81,7 @@
|
|||||||
"helpBtn":"帮付按钮名称",
|
"helpBtn":"帮付按钮名称",
|
||||||
"helpBtnPlaceholder":"请输入帮付按钮名称",
|
"helpBtnPlaceholder":"请输入帮付按钮名称",
|
||||||
"remark":"发起帮付留言",
|
"remark":"发起帮付留言",
|
||||||
"remarkPlaceholder":"请输入留言备注"
|
"remarkPlaceholder":"请输入留言备注",
|
||||||
|
"payWechatImage":"默认分享图片(公众号)",
|
||||||
|
"payWeappImage":"默认分享图片(小程序)"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,5 +27,7 @@
|
|||||||
"appPublicCertPathTips": "上传appCertPublicKey文件",
|
"appPublicCertPathTips": "上传appCertPublicKey文件",
|
||||||
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
|
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
|
||||||
"alipayRootCertPathTips": "上传alipayRootCert文件",
|
"alipayRootCertPathTips": "上传alipayRootCert文件",
|
||||||
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景"
|
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景",
|
||||||
|
"transferTips":"注意:应微信方规定,在2025年1月15日前开通商家转账到零钱服务的商户号可正常使用转账功能,之后开通的不支持使用转账到零钱服务"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
"type":"插件类型",
|
"type":"插件类型",
|
||||||
"typePlaceholder":"请选择插件类型",
|
"typePlaceholder":"请选择插件类型",
|
||||||
"typePlaceholder1":"应用:指独立开发的系统,比如商城,零售,erp等",
|
"typePlaceholder1":"应用:指独立开发的系统,比如商城,零售,erp等",
|
||||||
"typePlaceholder2":"插件:指不是独立的系统,可以是辅助应用的插件比如商城的拼团,也可以是独立的插件比如系统表单等",
|
"typePlaceholder2":"插件:指不是独立的系统,可以是辅助应用的插件比如商城的拼团,也可以是独立的插件比如万能表单等",
|
||||||
"supportType":"所属应用",
|
"supportType":"所属应用",
|
||||||
"supportApp":"支持应用",
|
"supportApp":"支持应用",
|
||||||
"supportAppPlaceholder":"请选择支持应用",
|
"supportAppPlaceholder":"请选择支持应用",
|
||||||
|
|||||||
@ -13,5 +13,7 @@
|
|||||||
"batchEmptySelectedCronLogTips": "请选择要删除的日志",
|
"batchEmptySelectedCronLogTips": "请选择要删除的日志",
|
||||||
"batchDeleteTips": "确定要删除选中的日志吗?",
|
"batchDeleteTips": "确定要删除选中的日志吗?",
|
||||||
"clearAllTips": "确定要清空所有日志吗?",
|
"clearAllTips": "确定要清空所有日志吗?",
|
||||||
"deleteTips": "确定要删除该条日志吗?"
|
"deleteTips": "确定要删除该条日志吗?",
|
||||||
|
"startDate": "开始日期",
|
||||||
|
"endDate": "结束日期"
|
||||||
}
|
}
|
||||||
@ -3,40 +3,40 @@
|
|||||||
<div class="main-container" v-loading="loading">
|
<div class="main-container" v-loading="loading">
|
||||||
<el-card class="box-card !border-none" shadow="never">
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
|
||||||
<div class="flex justify-between items-center">
|
<template v-if="Object.keys(appList).length">
|
||||||
<span class="text-page-title">应用管理</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="appList.length">
|
<template v-for="(item, index) in appList" :key="index + 'b'">
|
||||||
<div v-for="(item, index) in appList" :key="index + 'b'" class="cursor-pointer mt-[20px] mr-4 bg-[#f7f7f7]" @click="toLink(item.key)">
|
<div class="flex justify-between items-center">
|
||||||
<el-tooltip class="box-item" effect="light" placement="top">
|
<span class="text-page-title">{{ item.title }}</span>
|
||||||
<template #content>
|
</div>
|
||||||
<div class="max-w-[250px]">{{item.desc}}</div>
|
|
||||||
</template>
|
<div class="flex flex-wrap plug-list pb-10 plug-large">
|
||||||
<div class="w-[264px] flex py-[20px] px-[17px] app-item relative">
|
<div class="cursor-pointer mt-[20px] mr-4 bg-[#f7f7f7]" v-for="(childItem,childIndex) in item.list" :key="childIndex" @click="toLink(childItem)">
|
||||||
<el-image class="w-[40px] h-[40px] mr-[10px]" :src="img(item.icon)" fit="contain">
|
<div class="w-[264px] flex py-[20px] px-[17px] app-item relative">
|
||||||
<template #error>
|
<el-image class="w-[40px] h-[40px] mr-[10px]" :src="img(childItem.icon)" fit="contain">
|
||||||
<div class="image-slot">
|
<template #error>
|
||||||
<img class="w-[40px] h-[40px]" src="@/app/assets/images/index/app_default.png" />
|
<div class="image-slot">
|
||||||
|
<img class="w-[40px] h-[40px]" src="@/app/assets/images/index/app_default.png" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<div class="flex flex-col justify-between w-[180px]">
|
||||||
|
<div class="text-[14px] flex items-center">
|
||||||
|
<span class="app-text max-w-[170px]">{{ childItem.title }}</span>
|
||||||
|
<span class="iconfont iconxiaochengxu2 text-[#00b240] ml-[4px] !text-[14px]"></span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<!-- <el-icon color="#666">
|
||||||
</el-image>
|
<QuestionFilled />
|
||||||
<div class="flex flex-col justify-between w-[180px]">
|
</el-icon> -->
|
||||||
<div class="text-[14px] flex items-center">
|
<p class="app-text text-[12px] text-[#999]">{{childItem.desc}}</p>
|
||||||
<span class="app-text max-w-[170px]">{{ item.title }}</span>
|
|
||||||
<span class="iconfont iconxiaochengxu2 text-[#00b240] ml-[4px] !text-[14px]"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <el-icon color="#666">
|
|
||||||
<QuestionFilled />
|
|
||||||
</el-icon> -->
|
|
||||||
<p class="app-text text-[12px] text-[#999]">{{item.desc}}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<div class="empty flex items-center justify-center" v-if="!loading && !appList.length">
|
<div class="empty flex items-center justify-center" v-if="!loading && !Object.keys(appList).length">
|
||||||
<el-empty :description="t('emptyAppData')" />
|
<el-empty :description="t('emptyAppData')" />
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { getSiteAddons } from '@/app/api/site'
|
import { getSiteAddons,getShowApp } from '@/app/api/site'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -57,14 +57,25 @@ const appList = ref<Record<string, any>[]>([])
|
|||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const getAppList = async () => {
|
const getAppList = async () => {
|
||||||
const res = await getSiteAddons()
|
// const res = await getSiteAddons()
|
||||||
|
// appList.value = res.data
|
||||||
|
// loading.value = false
|
||||||
|
|
||||||
|
const res = await getShowApp();
|
||||||
|
console.log('app',res)
|
||||||
appList.value = res.data
|
appList.value = res.data
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
console.log('appList.value',appList.value,appList.value.length)
|
||||||
}
|
}
|
||||||
getAppList()
|
getAppList()
|
||||||
|
|
||||||
const toLink = (addon: string) => {
|
const toLink = (item: any) => {
|
||||||
addonIndexRoute[addon] && router.push({ name: addonIndexRoute[addon] })
|
console.log('tol', item)
|
||||||
|
if (item.url) {
|
||||||
|
router.push(item.url)
|
||||||
|
} else {
|
||||||
|
addonIndexRoute[item.key] && router.push({ name: addonIndexRoute[item.key] })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
<el-form-item :label="t('authId')" prop="api_url" v-show="formData.menu_type != 0">
|
<el-form-item :label="t('authId')" prop="api_url" v-show="formData.menu_type != 0">
|
||||||
<el-input v-model.trim="formData.api_url" :placeholder="t('authIdPlaceholder')" class="input-width">
|
<el-input v-model.trim="formData.api_url" :placeholder="t('authIdPlaceholder')" class="input-width">
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-select class="w-[90px] border-none" v-model="formData.methods">
|
<el-select class="border-none" style="width: 100px" v-model="formData.methods">
|
||||||
<el-option label="POST" value="post" />
|
<el-option label="POST" value="post" />
|
||||||
<el-option label="GET" value="get" />
|
<el-option label="GET" value="get" />
|
||||||
<el-option label="PUT" value="put" />
|
<el-option label="PUT" value="put" />
|
||||||
@ -77,7 +77,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('sort')">
|
<el-form-item :label="t('sort')">
|
||||||
<el-input-number v-model="formData.sort" :min="0" max="8" />
|
<el-input-number v-model="formData.sort" :min="0"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
|||||||
119
admin/src/app/views/diy/components/add-theme.vue
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogThemeVisible" title="新增颜色" width="550px" align-center>
|
||||||
|
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules">
|
||||||
|
<el-form-item label="名字" prop="title">
|
||||||
|
<el-input v-model="formData.title" class="!w-[250px]" maxlength="7" placeholder="请输入颜色名称" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="颜色key值" prop="label">
|
||||||
|
<el-input v-model="formData.label" class="!w-[250px]" maxlength="20" :disabled="type=='edit'" placeholder="请输入颜色key值" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="颜色value值" prop="value">
|
||||||
|
<el-color-picker v-model="formData.value" show-alpha :predefine="diyStore.predefineColors"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="颜色提示">
|
||||||
|
<el-input v-model="formData.tip" class="!w-[250px]" placeholder="请输入颜色提示" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="dialogThemeVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirmFn(formRef)">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, watch } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { getAreaListByPid } from '@/app/api/sys'
|
||||||
|
import { addMemberAddress } from '@/app/api/member'
|
||||||
|
import { filterNumber } from '@/utils/common'
|
||||||
|
import type { FormInstance } from 'element-plus'
|
||||||
|
import { FormRules } from 'element-plus'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
|
||||||
|
const dialogThemeVisible = ref(false)
|
||||||
|
let confirmRepeat = false
|
||||||
|
const emit = defineEmits(['confirm'])
|
||||||
|
/**
|
||||||
|
* 表单数据
|
||||||
|
*/
|
||||||
|
const initialData = {
|
||||||
|
title: '',
|
||||||
|
label: '',
|
||||||
|
value: '',
|
||||||
|
tip: ''
|
||||||
|
}
|
||||||
|
let keyArr = []; // 存储现有颜色的key
|
||||||
|
let type = ref('') // 区分编辑和添加
|
||||||
|
const formData: Record<string, any> = reactive({ ...initialData })
|
||||||
|
|
||||||
|
const open = (option:any) => {
|
||||||
|
keyArr = option.key;
|
||||||
|
type.value = '';
|
||||||
|
// 恢复默认值
|
||||||
|
for(let key in formData){
|
||||||
|
formData[key] = ''
|
||||||
|
}
|
||||||
|
if(option.data && Object.keys(option.data).length){
|
||||||
|
type.value = 'edit';
|
||||||
|
Object.keys(formData).forEach((item,index)=>{
|
||||||
|
formData[item] = option.data[item] ? option.data[item] : '';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
dialogThemeVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const formRules = reactive<FormRules>({
|
||||||
|
title: [
|
||||||
|
{ required: true, message: "请输入颜色名称", trigger: 'blur' }
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{ required: true, message: "请输入颜色value值", trigger: 'blur' }
|
||||||
|
],
|
||||||
|
label: [
|
||||||
|
{ required: true, message: "请输入颜色key值", trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule: any, value: any, callback: any) => {
|
||||||
|
const regex = /^[a-zA-Z0-9-]+$/
|
||||||
|
if (keyArr.indexOf(value) != -1) {
|
||||||
|
callback('新增颜色key值与已存在颜色key值命名重复,请修改命名')
|
||||||
|
} if (!regex.test(value)) {
|
||||||
|
callback('颜色key值只能输入字母、数字和连字符')
|
||||||
|
} else{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const confirmFn = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (confirmRepeat || !formEl) return
|
||||||
|
await formEl.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
emit('confirm', cloneDeep(formData));
|
||||||
|
dialogThemeVisible.value = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
dialogThemeVisible,
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<div class="edit-attr-item-wrap">
|
<div class="edit-attr-item-wrap">
|
||||||
<h3 class="mb-[10px]">{{ t('titleContent') }}</h3>
|
<h3 class="mb-[10px]">{{ t('titleContent') }}</h3>
|
||||||
<el-form label-width="80px" class="px-[10px]">
|
<el-form label-width="80px" class="px-[10px]" @submit.prevent>
|
||||||
<el-form-item :label="t('selectStyle')" class="flex">
|
<el-form-item :label="t('selectStyle')" class="flex">
|
||||||
<span class="text-primary flex-1 cursor-pointer" @click="showTitleStyle">{{ diyStore.editComponent.titleStyle.title }}</span>
|
<span class="text-primary flex-1 cursor-pointer" @click="showTitleStyle">{{ diyStore.editComponent.titleStyle.title }}</span>
|
||||||
<el-icon>
|
<el-icon>
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<div class="edit-attr-item-wrap">
|
<div class="edit-attr-item-wrap">
|
||||||
<h3 class="mb-[10px]">{{ t('carouselSearchSet') }}</h3>
|
<h3 class="mb-[10px]">{{ t('carouselSearchSet') }}</h3>
|
||||||
<el-form label-width="100px" class="px-[10px]">
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
<el-form-item :label="t('selectStyle')" class="flex">
|
<el-form-item :label="t('selectStyle')" class="flex">
|
||||||
<span class="text-primary flex-1 cursor-pointer" @click="showSearchStyle">{{ diyStore.editComponent.search.styleName }}</span>
|
<span class="text-primary flex-1 cursor-pointer" @click="showSearchStyle">{{ diyStore.editComponent.search.styleName }}</span>
|
||||||
<el-icon>
|
<el-icon>
|
||||||
@ -101,7 +101,7 @@
|
|||||||
<el-collapse v-model="activeNames" @change="handleChange" class="collapse-wrap">
|
<el-collapse v-model="activeNames" @change="handleChange" class="collapse-wrap">
|
||||||
<el-collapse-item :title="t('carouselSearchTabSet')" name="tab">
|
<el-collapse-item :title="t('carouselSearchTabSet')" name="tab">
|
||||||
<div class="edit-attr-item-wrap">
|
<div class="edit-attr-item-wrap">
|
||||||
<el-form label-width="100px" class="px-[10px]">
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
<el-form-item :label="t('carouselSearchTabControl')">
|
<el-form-item :label="t('carouselSearchTabControl')">
|
||||||
<el-switch v-model="diyStore.editComponent.tab.control" />
|
<el-switch v-model="diyStore.editComponent.tab.control" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -139,7 +139,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 选择微页面弹出框 -->
|
<!-- 选择微页面弹出框 -->
|
||||||
<el-dialog v-model="diyPageShowDialog" :title="t('selectSourcesDiyPage')" width="1000px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
|
<el-dialog v-model="diyPageShowDialog" :title="t('selectSourcesDiyPage')" width="1000px" :close-on-press-escape="true" :destroy-on-close="true" :close-on-click-modal="false">
|
||||||
<el-table :data="diyPageTable.data" ref="diyPageTableRef" size="large" v-loading="diyPageTable.loading" height="490px" @current-change="handleCurrentDiyPageChange" row-key="id" highlight-current-row>
|
<el-table :data="diyPageTable.data" ref="diyPageTableRef" size="large" v-loading="diyPageTable.loading" height="490px" @current-change="handleCurrentDiyPageChange" row-key="id" highlight-current-row>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<span>{{ !diyPageTable.loading ? t('emptyData') : '' }}</span>
|
<span>{{ !diyPageTable.loading ? t('emptyData') : '' }}</span>
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 内容 -->
|
<!-- 内容 -->
|
||||||
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
<div class="edit-attr-item-wrap">
|
<div class="edit-attr-item-wrap">
|
||||||
<h3 class="mb-[10px]">{{ t('pageContent') }}</h3>
|
<h3 class="mb-[10px]">{{ t('pageContent') }}</h3>
|
||||||
<el-form label-width="80px" class="px-[10px]">
|
<el-form label-width="80px" class="px-[10px]" @submit.prevent>
|
||||||
<el-form-item :label="t('diyPageTitle')">
|
<el-form-item :label="t('diyPageTitle')">
|
||||||
<el-input v-model.trim="diyStore.pageTitle" :placeholder="t('diyPageTitlePlaceholder')" clearable maxlength="12" show-word-limit/>
|
<el-input v-model.trim="diyStore.pageTitle" :placeholder="t('diyPageTitlePlaceholder')" clearable maxlength="16" show-word-limit/>
|
||||||
<div class="text-sm text-gray-400">{{ t('pageTitleTips') }}</div>
|
<div class="text-sm text-gray-400">{{ t('pageTitleTips') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单布局 页面设置 -->
|
||||||
|
<slot name="content"></slot>
|
||||||
|
|
||||||
<div class="edit-attr-item-wrap">
|
<div class="edit-attr-item-wrap">
|
||||||
<h3 class="mb-[10px]">{{ t('statusBarContent') }}</h3>
|
<h3 class="mb-[10px]">{{ t('statusBarContent') }}</h3>
|
||||||
<el-form label-width="80px" class="px-[10px]">
|
<el-form label-width="80px" class="px-[10px]" @submit.prevent>
|
||||||
<el-form-item :label="t('topStatusBarNav')" class="display-block">
|
<el-form-item :label="t('topStatusBarNav')" class="display-block">
|
||||||
<el-switch v-model="diyStore.global.topStatusBar.isShow"/>
|
<el-switch v-model="diyStore.global.topStatusBar.isShow"/>
|
||||||
<div class="text-sm text-gray-400">{{ t('statusBarSwitchTips') }}</div>
|
<div class="text-sm text-gray-400">{{ t('statusBarSwitchTips') }}</div>
|
||||||
|
|||||||
258
admin/src/app/views/diy/components/edit-theme.vue
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogThemeVisible" title="编辑色调" width="850px" align-center>
|
||||||
|
<el-form :model="openData" label-width="150px" :rules="formRules" class="h-[640px] overflow-auto" ref="formRef" @submit.prevent>
|
||||||
|
|
||||||
|
<el-form-item label="色调名称" prop="title" >
|
||||||
|
<el-input v-model="openData.title" placeholder="请输入色调名称" maxlength="15" class="!w-[250px]" :disabled="openData.mark != 'diy'" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="item.title" v-for="(item,index) in formData" :key="index">
|
||||||
|
<el-color-picker v-model="item.value" show-alpha :predefine="diyStore.predefineColors"/>
|
||||||
|
<div class="form-tip">{{item.tip}}</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="item.title" v-for="(item,index) in openData.diy_value" :key="index">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<el-color-picker v-model="item.value" show-alpha :predefine="diyStore.predefineColors"/>
|
||||||
|
<span class="text-primary cursor-pointer text-[14px] ml-[20px]" @click="editThemeFn(item)">编辑</span>
|
||||||
|
<span class="text-primary cursor-pointer text-[14px] ml-[8px]" @click="deleteThemeFn(item)">删除</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-tip">{{item.tip}}</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<div class="flex items-center text-primary cursor-pointer text-[14px]" @click="addThemeFn">
|
||||||
|
<span class="mr-[3px]">+</span>
|
||||||
|
<span>新增颜色</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-tip">新增颜色key值不能与当前的存在的key值重复</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<add-theme ref="addThemeRef" @confirm="addThemeConfirm" />
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="dialogThemeVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" plain @click="resetConfirmFn()">重置</el-button>
|
||||||
|
<el-button type="primary" @click="confirmFn(formRef)">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, watch } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { filterNumber } from '@/utils/common'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import addTheme from './add-theme.vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import type { FormInstance } from 'element-plus'
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
|
||||||
|
const dialogThemeVisible = ref(false)
|
||||||
|
const addThemeRef = ref(null)
|
||||||
|
const openData: Record<string, any> = reactive({ // 用于接收弹窗打开时的参数
|
||||||
|
title: '',
|
||||||
|
mark: '',
|
||||||
|
diy_value: [],
|
||||||
|
default: {},
|
||||||
|
data: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['confirm'])
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const formRules = computed(() => {
|
||||||
|
return {
|
||||||
|
title: [
|
||||||
|
{ required: true, message: "请输入色调名称", trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单数据
|
||||||
|
*/
|
||||||
|
const initialFormData = [
|
||||||
|
{
|
||||||
|
title: '主色调',
|
||||||
|
label: '--primary-color',
|
||||||
|
value: '#333333',
|
||||||
|
tip: '主色调在uiapp中使用:var(--primary-color)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '辅色调',
|
||||||
|
label: '--primary-help-color',
|
||||||
|
value: '#333333',
|
||||||
|
tip: '辅色调在uiapp中使用:var(--primary-help-color)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '页面背景色',
|
||||||
|
label: '--page-bg-color',
|
||||||
|
value: '#ffffff',
|
||||||
|
tip: '页面背景色在uiapp中使用:var(--page-bg-color)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '主色调浅色(淡)',
|
||||||
|
label: '--primary-color-light',
|
||||||
|
value: '',
|
||||||
|
tip: '主色调浅色(淡)在uiapp中使用:var(--primary-color-light)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '主色调浅色(深)',
|
||||||
|
label: '--primary-color-light2',
|
||||||
|
value: '',
|
||||||
|
tip: '主色调浅色(深)在uiapp中使用:var(--primary-color-light2)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '灰色调',
|
||||||
|
label: '--primary-color-dark',
|
||||||
|
value: '#cccccc',
|
||||||
|
tip: '灰色调在uiapp中使用:var(--primary-color-dark)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '禁用色',
|
||||||
|
label: '--primary-color-disabled',
|
||||||
|
value: '#eeeeee',
|
||||||
|
tip: '禁用色在uiapp中使用:var(--primary-color-disabled)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '价格颜色',
|
||||||
|
label: '--price-text-color',
|
||||||
|
value: '#333333',
|
||||||
|
tip: '价格颜色在uiapp中使用:var(--price-text-color)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const formData = ref([...cloneDeep(initialFormData)])
|
||||||
|
|
||||||
|
const open = (res:any) => { //参数: name=>色调名称,key=>区分系统还是应用的标识,default=>色调颜色的默认值,用于重置,data=>当前色调颜色值
|
||||||
|
Object.keys(openData).forEach((key: string) => {
|
||||||
|
openData[key] = res[key] != undefined ? cloneDeep(res[key]) : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 恢复默认值
|
||||||
|
formData.value.forEach((item,index) => {
|
||||||
|
initialFormData.forEach((subItem, subIndex)=>{
|
||||||
|
if(item.label == subItem.label){
|
||||||
|
item.value = subItem.value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染值
|
||||||
|
formData.value.forEach((item,index) => {
|
||||||
|
item.value = res.data[item.label] ? res.data[item.label] : item.value
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogThemeVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增颜色
|
||||||
|
const addThemeFn = ()=>{
|
||||||
|
let keyArr = []
|
||||||
|
formData.value.forEach((item,index) => {
|
||||||
|
keyArr.push(item.label);
|
||||||
|
});
|
||||||
|
let obj = {
|
||||||
|
key: keyArr
|
||||||
|
}
|
||||||
|
addThemeRef.value.open(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑颜色
|
||||||
|
const editThemeFn = (res:any)=>{
|
||||||
|
let keyArr = []
|
||||||
|
formData.value.forEach((item,index) => {
|
||||||
|
keyArr.push(item.label);
|
||||||
|
});
|
||||||
|
let obj = {
|
||||||
|
key: keyArr,
|
||||||
|
data: res
|
||||||
|
}
|
||||||
|
addThemeRef.value.open(obj);
|
||||||
|
}
|
||||||
|
// 删除颜色
|
||||||
|
const deleteThemeFn = (res:any)=>{
|
||||||
|
let indent = -1;
|
||||||
|
for(let i = 0; i < openData.diy_value.length; i++){
|
||||||
|
if(openData.diy_value[i].label == res.label){
|
||||||
|
indent = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(indent > -1){
|
||||||
|
openData.diy_value.splice(indent,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加颜色组件回调
|
||||||
|
const addThemeConfirm = (res:any) =>{
|
||||||
|
for(let i = 0; i < openData.diy_value.length; i++){
|
||||||
|
if(openData.diy_value[i].label == res.label){
|
||||||
|
openData.diy_value[i] = res;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
openData.diy_value.push(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置当前配色
|
||||||
|
const resetConfirmFn = ()=>{
|
||||||
|
if(openData.default && Object.keys(openData.default).length){
|
||||||
|
formData.value.forEach((item,index)=>{
|
||||||
|
item.value = cloneDeep(openData.default[item.label]);
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
formData.value = cloneDeep(initialFormData);
|
||||||
|
}
|
||||||
|
|
||||||
|
openData.diy_value = [];
|
||||||
|
|
||||||
|
if(openData.mark == 'diy'){
|
||||||
|
openData.title = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage({
|
||||||
|
message: '重置成功',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const confirmFn = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
let params = {
|
||||||
|
theme: {},
|
||||||
|
diy_value: [],
|
||||||
|
title: ''
|
||||||
|
}
|
||||||
|
params.title = openData.title;
|
||||||
|
|
||||||
|
formData.value.forEach((item,index) => {
|
||||||
|
params.theme[item.label] = item.value
|
||||||
|
});
|
||||||
|
openData.diy_value.forEach((item,index) => {
|
||||||
|
params.theme[item.label] = item.value
|
||||||
|
});
|
||||||
|
|
||||||
|
params.diy_value = openData.diy_value || [];
|
||||||
|
|
||||||
|
emit('confirm', params);
|
||||||
|
|
||||||
|
dialogThemeVisible.value = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
dialogThemeVisible,
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
174
admin/src/app/views/diy/components/theme-list.vue
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogThemeVisible" :title="data.addon_title" width="550px" align-center>
|
||||||
|
<el-form class="page-form mt-[15px]" :model="formData" label-width="90px" v-loading="loading">
|
||||||
|
<el-form-item label="选择配色">
|
||||||
|
<div class="flex items-center flex-wrap">
|
||||||
|
<template v-for="(tempItem,tempIndex) in theme_temp">
|
||||||
|
<div :key="tempIndex" v-if="tempItem.name != 'diy'" class="flex items-center border-[1px] border-solid border-[#dcdee2] rounded-[5px] h-[40px] px-[15px] mr-[10px] cursor-pointer my-[5px]" :class="{'!border-[var(--el-color-primary)]': curr_theme_mark == tempItem.name}" @click="themeTempChange(tempItem)">
|
||||||
|
<span v-if="data.theme" class="w-[20px] h-[20px] mr-[5px] rounded-[3px]" :style="{backgroundColor: data.theme['--primary-color']}"></span>
|
||||||
|
<span class="text-[14px]" :class="{'!text-[var(--el-color-primary)]': curr_theme_mark == tempItem.name}">{{tempItem.title}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="flex items-center border-[1px] border-solid border-[#dcdee2] rounded-[5px] h-[40px] px-[15px] cursor-pointer" :class="{'!border-[var(--el-color-primary)]': curr_theme_mark == 'diy'}" @click="themeTempChange('diy')">
|
||||||
|
<span class="nc-iconfont nc-icon-tianjiaV6xx mr-[5px]" :class="{'!text-[var(--el-color-primary)]': curr_theme_mark == 'diy'}"></span>
|
||||||
|
<span class="text-[14px]" :class="{'!text-[var(--el-color-primary)]': curr_theme_mark == 'diy'}">自定义</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<edit-theme ref="editThemeRef" @confirm="editThemeConfirm"/>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="dialogThemeVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" plain @click="editThemeFn()">编辑</el-button>
|
||||||
|
<el-button type="primary" @click="confirmFn()">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, watch } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { setDiyTheme, getDefaultTheme } from '@/app/api/diy'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import editTheme from './edit-theme.vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import { time } from 'echarts'
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
|
||||||
|
const editThemeRef = ref(null)
|
||||||
|
const dialogThemeVisible = ref(false)
|
||||||
|
let confirmRepeat = false
|
||||||
|
const curr_theme_title = ref('') //当前配色title
|
||||||
|
const curr_theme_mark = ref('') //当前配色标识
|
||||||
|
const curr_theme_value = ref('') //当前配色theme
|
||||||
|
const theme_temp = ref([]);
|
||||||
|
const mode = ref('default'); // 当前模式
|
||||||
|
|
||||||
|
const data = ref({})
|
||||||
|
const open = (res:any) => {
|
||||||
|
|
||||||
|
confirmRepeat = false;
|
||||||
|
data.value = cloneDeep(res);
|
||||||
|
curr_theme_value.value = res.value;
|
||||||
|
curr_theme_mark.value = res.color_mark;
|
||||||
|
curr_theme_title.value = res.color_name;
|
||||||
|
|
||||||
|
// 新增颜色
|
||||||
|
theme_temp.value.forEach((item,index)=>{
|
||||||
|
if(item.name == data.value.color_mark){
|
||||||
|
item.diy_value = data.value.diy_value;
|
||||||
|
item.title = res.color_name;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mode.value = res.mode;
|
||||||
|
dialogThemeVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['confirm'])
|
||||||
|
|
||||||
|
const initData = () => {
|
||||||
|
getDefaultTheme().then((res) => {
|
||||||
|
theme_temp.value = res.data || [];
|
||||||
|
|
||||||
|
// 将自定义添加到里面
|
||||||
|
let diy_theme_temp = {
|
||||||
|
name: 'diy',
|
||||||
|
theme: '',
|
||||||
|
title: ''
|
||||||
|
}
|
||||||
|
theme_temp.value.push(diy_theme_temp);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
initData()
|
||||||
|
|
||||||
|
// 切换不同配色
|
||||||
|
const themeTempChange = (item)=>{
|
||||||
|
if(item.name == data.value.color_mark){ // 选择默认配色的情况
|
||||||
|
curr_theme_title.value = data.value.color_name;
|
||||||
|
curr_theme_mark.value = data.value.color_mark;
|
||||||
|
curr_theme_value.value = data.value.value;
|
||||||
|
}else if(typeof item == 'object'){ // 选择除默认配色的情况
|
||||||
|
curr_theme_title.value = item.title;
|
||||||
|
curr_theme_mark.value = item.name;
|
||||||
|
curr_theme_value.value = item.theme;
|
||||||
|
}else{ // 自定义情况
|
||||||
|
curr_theme_title.value = '自定义';
|
||||||
|
curr_theme_mark.value = item;
|
||||||
|
curr_theme_value.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑色调
|
||||||
|
const editThemeFn = ()=>{
|
||||||
|
let theme = {
|
||||||
|
default: {}, // 当前色调的默认值
|
||||||
|
data: {}, // 当前色调
|
||||||
|
title:'',
|
||||||
|
mark: '', // 标识,区分是自定义还是模版色调,
|
||||||
|
diy_value: [] // 新增颜色值
|
||||||
|
}
|
||||||
|
theme.data = cloneDeep(curr_theme_value.value) || {};
|
||||||
|
theme.mark = curr_theme_mark.value;
|
||||||
|
theme_temp.value.forEach((item,index)=>{
|
||||||
|
if(item.name == curr_theme_mark.value){
|
||||||
|
theme.default = item.theme ? cloneDeep(item.theme) : '';
|
||||||
|
theme.diy_value= item.diy_value || [];
|
||||||
|
theme.title = item.title;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
editThemeRef.value.open(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑色调回调
|
||||||
|
const editThemeConfirm = (res)=>{
|
||||||
|
if(curr_theme_mark.value == data.value.color_mark){
|
||||||
|
data.value.value = res.theme;
|
||||||
|
}
|
||||||
|
theme_temp.value.forEach((item,index)=>{
|
||||||
|
if(item.name == curr_theme_mark.value){
|
||||||
|
item.diy_value= res.diy_value || [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
data.value.title = res.title;
|
||||||
|
curr_theme_value.value = res.theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击保存
|
||||||
|
const confirmFn = () => {
|
||||||
|
if (confirmRepeat) return
|
||||||
|
confirmRepeat = true
|
||||||
|
let params = {}
|
||||||
|
params.id = data.value.id;
|
||||||
|
params.mode = mode.value;
|
||||||
|
params.color_mark = curr_theme_mark.value;
|
||||||
|
params.value = curr_theme_value.value;
|
||||||
|
params.key = data.value.key;
|
||||||
|
params.color_name = curr_theme_mark.value == 'diy' ? (data.value.title || '自定义') : curr_theme_title.value;
|
||||||
|
theme_temp.value.forEach((item,index)=>{
|
||||||
|
if(item.name == curr_theme_mark.value){
|
||||||
|
params.diy_value = cloneDeep(item.diy_value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setDiyTheme(params).then((res) => {
|
||||||
|
emit('confirm', data);
|
||||||
|
confirmRepeat = false;
|
||||||
|
dialogThemeVisible.value = false;
|
||||||
|
}).catch(()=>{
|
||||||
|
confirmRepeat = false;
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
dialogThemeVisible,
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -138,7 +138,7 @@
|
|||||||
|
|
||||||
<div class="edit-component-wrap">
|
<div class="edit-component-wrap">
|
||||||
|
|
||||||
<component v-if="diyStore.currentComponent" :is="modules[diyStore.currentComponent]" :value="diyStore.value[diyStore.currentIndex]">
|
<component v-if="diyStore.currentComponent" :is="modules[diyStore.currentComponent]" :key="diyStore.currentIndex" :value="diyStore.value[diyStore.currentIndex]">
|
||||||
<template #style>
|
<template #style>
|
||||||
<div class="edit-attr-item-wrap">
|
<div class="edit-attr-item-wrap">
|
||||||
<h3 class="mb-[10px]">{{ t('componentStyleTitle') }}</h3>
|
<h3 class="mb-[10px]">{{ t('componentStyleTitle') }}</h3>
|
||||||
@ -300,9 +300,6 @@ const modulesFiles = import.meta.glob('./components/*.vue', { eager: true })
|
|||||||
const addonModulesFiles = import.meta.glob('@/addon/**/views/diy/components/*.vue', { eager: true })
|
const addonModulesFiles = import.meta.glob('@/addon/**/views/diy/components/*.vue', { eager: true })
|
||||||
addonModulesFiles && Object.assign(modulesFiles, addonModulesFiles)
|
addonModulesFiles && Object.assign(modulesFiles, addonModulesFiles)
|
||||||
|
|
||||||
// todo 考虑用一个编辑页面实现,方便后期维护,根据路由判断,是微页面还是系统表单
|
|
||||||
// todo 系统表单可以使用自定义组件,微页面不能用系统表单组件
|
|
||||||
|
|
||||||
const modules = {}
|
const modules = {}
|
||||||
for (const [key, value] of Object.entries(modulesFiles)) {
|
for (const [key, value] of Object.entries(modulesFiles)) {
|
||||||
const moduleName = key.split('/').pop()
|
const moduleName = key.split('/').pop()
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog v-model="showDialog" :title="t('pageSelectTips')" width="400px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
|
<el-dialog v-model="showDialog" :title="t('pageSelectTips')" width="400px" :close-on-press-escape="true" :destroy-on-close="true" :close-on-click-modal="false">
|
||||||
<div class="flex items-start">
|
<div class="flex items-start">
|
||||||
<el-scrollbar class="pl-4 h-[300px] flex-1">
|
<el-scrollbar class="pl-4 h-[300px] flex-1">
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
|
|||||||
95
admin/src/app/views/diy/theme_style.vue
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<div class="main-container">
|
||||||
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-page-title">{{ pageName }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="data" size="large" class="mt-[20px]" v-loading="loading">
|
||||||
|
<template #empty>
|
||||||
|
<span>{{ !loading ? t('emptyData') : '' }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table-column label="应用" min-width="120" >
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<el-image class="w-[40px] h-[40px] rounded-md overflow-hidden" :src="img(row.icon)" fit="contain">
|
||||||
|
<template #error>
|
||||||
|
<div class="flex items-center w-full h-full">
|
||||||
|
<img class="w-full h-full" src="@/app/assets/images/icon-addon.png" alt="">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<div class="flex-1 ml-2 truncate">{{ row.addon_title }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="配色名称" min-width="120" >
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.color_name }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="配色方案" min-width="120" >
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="rounded-[3px] inline-flex items-center justify-center border-[1px] border-solid border-[#f2f2f2] overflow-hidden" v-if="row.value">
|
||||||
|
<span class="w-[18px] h-[18px]" :style="{backgroundColor: row.value['--primary-color']}"></span>
|
||||||
|
<span class="w-[18px] h-[18px]" :style="{backgroundColor: row.value['--primary-help-color']}"></span>
|
||||||
|
<span class="w-[18px] h-[18px]" :style="{backgroundColor: '#fff'}"></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column :label="t('operation')" align="right" fixed="right" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link @click="editEvent(row)">编辑</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<theme-list ref="themeListRef" @confirm="initData()" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, watch, computed } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { img } from '@/utils/common'
|
||||||
|
import { setDiyTheme, getDiyTheme, getDefaultTheme } from '@/app/api/diy'
|
||||||
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
import { ElMessage, FormInstance } from 'element-plus'
|
||||||
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import themeList from './components/theme-list.vue'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import { tr } from 'element-plus/es/locale'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const pageName = route.meta.title
|
||||||
|
const loading = ref(true)
|
||||||
|
const themeListRef = ref(null)
|
||||||
|
const data = ref([])
|
||||||
|
|
||||||
|
const initData = () => {
|
||||||
|
loading.value = true;
|
||||||
|
getDiyTheme().then((res) => {
|
||||||
|
let obj = cloneDeep(res.data);
|
||||||
|
for(let key in obj){
|
||||||
|
obj[key].key = key;
|
||||||
|
}
|
||||||
|
data.value = Object.values(obj);
|
||||||
|
loading.value = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
initData()
|
||||||
|
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const editEvent = (data)=>{
|
||||||
|
themeListRef.value.open(data);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-image v-for="(item,index) in props.data.handle_field_value" :src="img(item)" class="w-[70px] h-[70px]" :class="{ 'mr-[5px]' : (index + 1) < props.data.handle_field_value.length }" fit="contain" :preview-src-list="imgList" :zoom-rate="1.2" :max-scale="7"
|
||||||
|
:min-scale="0.2" :initial-index="index" :hide-on-click-modal="true" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
import { img } from '@/utils/common'
|
||||||
|
import { t } from "@/lang";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const imgList = computed(() => {
|
||||||
|
return props.data.handle_field_value.map((item: any) => {
|
||||||
|
return img(item)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div class="form-render">{{ props.data.render_value }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
import { t } from "@/lang";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-render {
|
||||||
|
word-wrap: break-word; /* 长单词或长字符串自动换行 */
|
||||||
|
word-break: break-word; /* 强制断词换行 */
|
||||||
|
white-space: pre-wrap; /* 保持空格和换行 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('地址格式')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.addressFormat" class="!block">
|
||||||
|
<el-radio class="!block" label="province/city/district/address">{{ t('省/市/区/街道/详细地址') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province/city/district/street">{{ t('省/市/区/街道(镇)') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province/city/district">{{ t('省/市/区(县)') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province/city">{{ t('省/市') }}</el-radio>
|
||||||
|
<el-radio class="!block" label="province">{{ t('省') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item class="display-block">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('隐私保护') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>会自动将提交的个人信息做加密展示。</p>
|
||||||
|
<p>适用于公开展示收集的数据且不暴露用户隐私。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.privacyProtection" />
|
||||||
|
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏地址,仅管理员可查看') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
194
admin/src/app/views/diy_form/components/edit-form-checkbox.vue
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('样式')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.style">
|
||||||
|
<el-radio label="style-1">{{ t('默认') }}</el-radio>
|
||||||
|
<el-radio label="style-2">{{ t('列表') }}</el-radio>
|
||||||
|
<el-radio label="style-3">{{ t('下拉') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('选项')">
|
||||||
|
<div ref="formCheckboxRef">
|
||||||
|
<div v-for="(option, index) in diyStore.editComponent.options" :key="option.id" class="option-item flex items-center mb-[15px]">
|
||||||
|
<el-input v-model="diyStore.editComponent.options[index].text" class="!w-[215px]" :placeholder="t('optionPlaceholder')" maxlength="30" clearable />
|
||||||
|
<span v-if="diyStore.editComponent.options.length > 1" @click="removeOption(index)" class="cursor-pointer ml-[5px] nc-iconfont nc-icon-shanchu-yuangaizhiV6xx"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]" @click="addOption">添加单个选项</span>
|
||||||
|
<el-popover :visible="visible" placement="bottom" :width="300">
|
||||||
|
<p class="mb-[5px]">批量添加选项</p>
|
||||||
|
<p class="text-[#888] text-[12px] mb-[5px]">每个选项之间用英文 "," 隔开,自动过滤重复内容</p>
|
||||||
|
<el-input v-model.trim="optionsValue" type="textarea" clearable maxlength="200" show-word-limit />
|
||||||
|
<div class="mt-[10px] text-right">
|
||||||
|
<el-button size="small" text @click="visible = false">取消</el-button>
|
||||||
|
<el-button size="small" type="primary" @click="batchAddOptions">确定</el-button>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<span class="text-primary cursor-pointer" @click="visible = true">批量添加选项</span>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<!-- <el-form label-width="100px" class="px-[10px]">-->
|
||||||
|
<!-- <el-form-item class="display-block">-->
|
||||||
|
<!-- <template #label>-->
|
||||||
|
<!-- <div class="flex items-center">-->
|
||||||
|
<!-- <span class="mr-[3px]">{{ t('隐私保护') }}</span>-->
|
||||||
|
<!-- <el-tooltip effect="light" placement="top">-->
|
||||||
|
<!-- <template #content>-->
|
||||||
|
<!-- <p>会自动将提交的个人信息做加密展示。</p>-->
|
||||||
|
<!-- <p>适用于公开展示收集的数据且不暴露用户隐私。</p>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-icon>-->
|
||||||
|
<!-- <QuestionFilled color="#999999" />-->
|
||||||
|
<!-- </el-icon>-->
|
||||||
|
<!-- </el-tooltip>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-switch v-model="diyStore.editComponent.field.privacyProtection" />-->
|
||||||
|
<!-- <div class="text-sm text-gray-400">{{ t('提交后自动隐藏内容,仅管理员可查看') }}</div>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
|
||||||
|
<!-- </el-form>-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref, onMounted, nextTick} from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
import { range } from 'lodash-es'
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
let pass = true;
|
||||||
|
for (let i = 0; i < diyStore.value[index].options.length; i++) {
|
||||||
|
if (!diyStore.value[index].options[i].text) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('optionPlaceholder');
|
||||||
|
pass = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pass) return res;
|
||||||
|
|
||||||
|
let uniqueOptions = uniqueByKey(diyStore.value[index].options, 'text')
|
||||||
|
if (uniqueOptions.length != diyStore.value[index].options.length) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('存在重复选项,请检查内容')
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
diyStore.editComponent.options.forEach((item: any) => {
|
||||||
|
if (!item.id) item.id = diyStore.generateRandom()
|
||||||
|
})
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const optionsValue = ref()
|
||||||
|
const addOption = () => {
|
||||||
|
diyStore.editComponent.options.push({
|
||||||
|
id: diyStore.generateRandom(),
|
||||||
|
text: '选项' + (diyStore.editComponent.options.length + 1)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeOption = (index:any) => {
|
||||||
|
diyStore.editComponent.options.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量添加选项
|
||||||
|
const batchAddOptions = () => {
|
||||||
|
if (optionsValue.value.trim()) {
|
||||||
|
const newOptions = optionsValue.value.split(',').map((option: any) => {
|
||||||
|
return {
|
||||||
|
id: diyStore.generateRandom(),
|
||||||
|
text: option.trim()
|
||||||
|
};
|
||||||
|
}).filter((option: any) => option.text !== '');
|
||||||
|
|
||||||
|
// 去除重复的选项
|
||||||
|
const uniqueNewOptions = uniqueByKey(newOptions, 'text');
|
||||||
|
|
||||||
|
// 过滤掉已存在的选项
|
||||||
|
const filteredNewOptions = uniqueNewOptions.filter((newOption: any) =>
|
||||||
|
!diyStore.editComponent.options.some((existingOption: any) => existingOption.text === newOption.text)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果有新的选项,添加到选项列表中
|
||||||
|
if (filteredNewOptions.length > 0) {
|
||||||
|
diyStore.editComponent.options.push(...filteredNewOptions);
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: "选项已存在,请重新输入",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsValue.value = '';
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 数组去重
|
||||||
|
const uniqueByKey = (arr: any, key: any) => {
|
||||||
|
const seen = new Set();
|
||||||
|
return arr.filter((item: any) => {
|
||||||
|
const serializedKey = JSON.stringify(item[key]);
|
||||||
|
return seen.has(serializedKey) ? false : seen.add(serializedKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const formCheckboxRef = ref()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
const sortable = Sortable.create(formCheckboxRef.value, {
|
||||||
|
group: 'option-item',
|
||||||
|
animation: 200,
|
||||||
|
onEnd: event => {
|
||||||
|
const temp = diyStore.editComponent.options[event.oldIndex!]
|
||||||
|
diyStore.editComponent.options.splice(event.oldIndex!, 1)
|
||||||
|
diyStore.editComponent.options.splice(event.newIndex!, 0, temp)
|
||||||
|
sortable.sort(
|
||||||
|
range(diyStore.editComponent.options.length).map(value => {
|
||||||
|
return value.toString()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
205
admin/src/app/views/diy_form/components/edit-form-date-scope.vue
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('时间格式')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.dateFormat">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<el-radio label="YYYY年M月D日">{{ dateFormat.format1 }}</el-radio>
|
||||||
|
<el-radio label="YYYY-MM-DD">{{ dateFormat.format2 }}</el-radio>
|
||||||
|
<el-radio label="YYYY/MM/DD">{{ dateFormat.format3 }}</el-radio>
|
||||||
|
</div>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('开始日期') }}</h3>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('提示语')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.start.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('默认值')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.start.defaultControl" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.start.defaultControl">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.start.dateWay">
|
||||||
|
<el-radio label="current">{{ t('当天日期') }}</el-radio>
|
||||||
|
<el-radio label="diy">{{ t('指定日期') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.start.defaultControl && diyStore.editComponent.start.dateWay == 'diy'">
|
||||||
|
<el-date-picker v-model="diyStore.editComponent.field.default.start.date" format="YYYY/MM/DD" value-format="YYYY-MM-DD" type="date" placeholder="请选择日期" @change="startDateChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('结束日期') }}</h3>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('提示语')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.end.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('默认值')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.end.defaultControl" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.end.defaultControl">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.end.dateWay">
|
||||||
|
<el-radio label="current">{{ t('当天日期') }}</el-radio>
|
||||||
|
<el-radio label="diy">{{ t('指定日期') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.end.defaultControl && diyStore.editComponent.end.dateWay == 'diy'">
|
||||||
|
<el-date-picker :disabled-date="disabledEndDate" v-model="diyStore.editComponent.field.default.end.date" format="YYYY/MM/DD" value-format="YYYY-MM-DD" type="date" placeholder="请选择结束日期" @change="endDateChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('文字样式') }}</h3>
|
||||||
|
<el-form label-width="80px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('textFontSize')">
|
||||||
|
<el-slider v-model="diyStore.editComponent.fontSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="12" :max="18" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('textFontWeight')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.fontWeight">
|
||||||
|
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
|
||||||
|
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('textColor')">
|
||||||
|
<el-color-picker v-model="diyStore.editComponent.textColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { timeTurnTimeStamp } from '@/utils/common'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
let starTime = diyStore.value[index].field.default.start.date;
|
||||||
|
let endTime = diyStore.value[index].field.default.end.date;
|
||||||
|
|
||||||
|
let today = new Date();
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
if(diyStore.editComponent.start.dateWay == 'current'){
|
||||||
|
starTime = today.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
if(diyStore.editComponent.end.dateWay == 'current'){
|
||||||
|
endTime = today.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(diyStore.editComponent.start.defaultControl && starTime == ''){
|
||||||
|
res.code = false
|
||||||
|
res.message = "开始日期不能为空"
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
if(diyStore.editComponent.end.defaultControl && endTime == ''){
|
||||||
|
res.code = false
|
||||||
|
res.message = "结束日期不能为空"
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
if(diyStore.editComponent.start.defaultControl && diyStore.editComponent.end.defaultControl && timeTurnTimeStamp(starTime) > timeTurnTimeStamp(endTime)){
|
||||||
|
res.code = false
|
||||||
|
res.message = "开始日期不能大于结束日期"
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const dateFormat: any = reactive({
|
||||||
|
format1: '',
|
||||||
|
format2: '',
|
||||||
|
format3: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 结束日期-禁止的日期
|
||||||
|
const disabledEndDate = (time:Date)=>{
|
||||||
|
let cutoffDate = null
|
||||||
|
let bool = false;
|
||||||
|
if(diyStore.editComponent.start && diyStore.editComponent.start.defaultControl){
|
||||||
|
if(diyStore.editComponent.start.dateWay == 'diy'){
|
||||||
|
cutoffDate = new Date(diyStore.editComponent.field.default.start.date);
|
||||||
|
}else{
|
||||||
|
cutoffDate = new Date();
|
||||||
|
}
|
||||||
|
bool = time.getTime() < cutoffDate.getTime();
|
||||||
|
}
|
||||||
|
return bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
let today = new Date();
|
||||||
|
let endDate = new Date();
|
||||||
|
endDate.setDate(endDate.getDate() + 7); // 设置日期为7天后的日期
|
||||||
|
|
||||||
|
if(diyStore.editComponent.field.default.start.timestamp){
|
||||||
|
diyStore.editComponent.field.default.start.date = today.toISOString().split('T')[0];
|
||||||
|
diyStore.editComponent.field.default.start.timestamp = parseInt(today.getTime() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(diyStore.editComponent.field.default.end.timestamp){
|
||||||
|
diyStore.editComponent.field.default.end.date = endDate.toISOString().split('T')[0];
|
||||||
|
diyStore.editComponent.field.default.end.timestamp = parseInt(endDate.getTime() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
let year = today.getFullYear();
|
||||||
|
let month = String(today.getMonth() + 1).padStart(2, '0');
|
||||||
|
let day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
dateFormat.format1 = `${year}年${month}月${day}日`;
|
||||||
|
dateFormat.format2 = `${year}-${month}-${day}`;
|
||||||
|
dateFormat.format3 = `${year}/${month}/${day}`;
|
||||||
|
dateFormat.format4 = `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 开始日期选择器
|
||||||
|
const startDateChange = (date)=>{
|
||||||
|
diyStore.editComponent.field.default.start.date = date;
|
||||||
|
diyStore.editComponent.field.default.start.timestamp = timeTurnTimeStamp(date);
|
||||||
|
|
||||||
|
let endDate = new Date(date)
|
||||||
|
endDate.setDate(endDate.getDate() + 7);
|
||||||
|
diyStore.editComponent.field.default.end.date = endDate.toISOString().split('T')[0];
|
||||||
|
diyStore.editComponent.field.default.end.timestamp = parseInt(endDate.getTime() / 1000);
|
||||||
|
}
|
||||||
|
// 结束日期选择器
|
||||||
|
const endDateChange = (date)=>{
|
||||||
|
diyStore.editComponent.field.default.end.date = date;
|
||||||
|
diyStore.editComponent.field.default.end.timestamp = timeTurnTimeStamp(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
101
admin/src/app/views/diy_form/components/edit-form-date.vue
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('日期格式')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.dateFormat" class="!block">
|
||||||
|
<el-radio class="!block" label="YYYY年M月D日">{{ dateFormat.format1 }}</el-radio>
|
||||||
|
<el-radio class="!block" label="YYYY-MM-DD">{{ dateFormat.format2 }}</el-radio>
|
||||||
|
<el-radio class="!block" label="YYYY/MM/DD">{{ dateFormat.format3 }}</el-radio>
|
||||||
|
<el-radio class="!block" label="YYYY-MM-DD HH:mm">{{ dateFormat.format4 }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('默认值')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.defaultControl"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.defaultControl">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.dateWay">
|
||||||
|
<el-radio label="current">{{ t('当天日期') }}</el-radio>
|
||||||
|
<el-radio label="diy">{{ t('指定日期') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.defaultControl && diyStore.editComponent.dateWay == 'diy'">
|
||||||
|
<el-date-picker v-if="diyStore.editComponent.dateFormat != 'YYYY-MM-DD HH:mm'" v-model="diyStore.editComponent.field.default.date" format="YYYY/MM/DD" value-format="YYYY-MM-DD" type="date" placeholder="请选择日期" @change="dateChange"/>
|
||||||
|
<el-date-picker v-else v-model="diyStore.editComponent.field.default.date" format="YYYY/MM/DD HH:mm" value-format="YYYY-MM-DD HH:mm" type="datetime" placeholder="请选择日期" @change="dateChange"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref,reactive,watch,onMounted } from 'vue'
|
||||||
|
import { timeTurnTimeStamp } from '@/utils/common'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const dateFormat: any = reactive({
|
||||||
|
format1: '',
|
||||||
|
format2: '',
|
||||||
|
format3: '',
|
||||||
|
format4: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始赋值当天日期
|
||||||
|
const today = new Date();
|
||||||
|
if (!diyStore.editComponent.field.default.date) {
|
||||||
|
diyStore.editComponent.field.default.date = today.toISOString().split('T')[0];
|
||||||
|
diyStore.editComponent.field.default.timestamp = today.getTime() / 1000;
|
||||||
|
}
|
||||||
|
let year = today.getFullYear();
|
||||||
|
let month = String(today.getMonth() + 1).padStart(2, '0');
|
||||||
|
let day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
dateFormat.format1 = `${year}年${month}月${day}日`;
|
||||||
|
dateFormat.format2 = `${year}-${month}-${day}`;
|
||||||
|
dateFormat.format3 = `${year}/${month}/${day}`;
|
||||||
|
dateFormat.format4 = `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const dateChange = (date: any)=>{
|
||||||
|
diyStore.editComponent.field.default.date = date;
|
||||||
|
diyStore.editComponent.field.default.timestamp = timeTurnTimeStamp(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
48
admin/src/app/views/diy_form/components/edit-form-email.vue
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
43
admin/src/app/views/diy_form/components/edit-form-file.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('限制上传大小')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.limitUploadSize" clearable maxlength="15" />
|
||||||
|
/单位MB,目前是Bit,要转换,*1024
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('内容防重复') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>该组件填写的内容不能与已提交的数据重复。</p>
|
||||||
|
<p>极端情况下可能存在延时导致限制失效。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.unique" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="display-block">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('隐私保护') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>会自动将提交的个人信息做加密展示。</p>
|
||||||
|
<p>适用于公开展示收集的数据且不暴露用户隐私。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.privacyProtection" />
|
||||||
|
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏中间11位数字,仅管理员可查看') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
88
admin/src/app/views/diy_form/components/edit-form-image.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('限制数量')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.limit" :placeholder="t('请输入限制数量')" clearable maxlength="2" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- <el-form-item :label="t('上传方式')">
|
||||||
|
<el-checkbox-group v-model="diyStore.editComponent.uploadMode" :min="1">
|
||||||
|
<el-checkbox label="拍照上传" value="take_pictures" />
|
||||||
|
<el-checkbox label="从相册选择" value="select_from_album" />
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-form-item> -->
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
if (diyStore.value[index].limit == '') {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('请输入限制数量')
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (isNaN(diyStore.value[index].limit) || !regExp.number.test(diyStore.value[index].limit)) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('限制数量格式输入错误');
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (diyStore.value[index].limit < 0) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('限制数量不能小于0')
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (diyStore.value[index].limit == 0) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('限制数量必须大于0')
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (diyStore.value[index].limit > 20) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('限制数量最大不能超过20')
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正则表达式
|
||||||
|
const regExp: any = {
|
||||||
|
required: /[\S]+/,
|
||||||
|
number: /^\d{0,10}$/,
|
||||||
|
digit: /^\d{0,10}(.?\d{0,2})$/,
|
||||||
|
special: /^\d{0,10}(.?\d{0,3})$/
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
99
admin/src/app/views/diy_form/components/edit-form-input.vue
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('defaultValue') }}</span>
|
||||||
|
<el-tooltip effect="light" :content="t('设置后,默认值会自动填充到输入框,填表人可在此基础上进行修改。')" placement="top">
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.field.default" :placeholder="t('defaultValuePlaceholder')" clearable maxlength="18" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('内容防重复') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>该组件填写的内容不能与已提交的数据重复。</p>
|
||||||
|
<p>极端情况下可能存在延时导致限制失效。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.unique" />
|
||||||
|
</el-form-item>
|
||||||
|
<!-- <el-form-item class="display-block">-->
|
||||||
|
<!-- <template #label>-->
|
||||||
|
<!-- <div class="flex items-center">-->
|
||||||
|
<!-- <span class="mr-[3px]">{{ t('隐私保护') }}</span>-->
|
||||||
|
<!-- <el-tooltip effect="light" placement="top">-->
|
||||||
|
<!-- <template #content>-->
|
||||||
|
<!-- <p>会自动将提交的个人信息做加密展示。</p>-->
|
||||||
|
<!-- <p>适用于公开展示收集的数据且不暴露用户隐私。</p>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-icon>-->
|
||||||
|
<!-- <QuestionFilled color="#999999" />-->
|
||||||
|
<!-- </el-icon>-->
|
||||||
|
<!-- </el-tooltip>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-switch v-model="diyStore.editComponent.field.privacyProtection" />-->
|
||||||
|
<!-- <div class="text-sm text-gray-400">{{ t('提交后自动隐藏文本,仅管理员可查看') }}</div>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('获取方式')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.mode">
|
||||||
|
<el-radio class="!mr-[20px]" label="authorized_wechat_location">{{ t('授权微信定位') }}</el-radio>
|
||||||
|
<el-radio label="open_choose_location">{{ t('手动选择定位') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item class="display-block">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('隐私保护') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>会自动将提交的个人信息做加密展示。</p>
|
||||||
|
<p>适用于公开展示收集的数据且不暴露用户隐私。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.privacyProtection" />
|
||||||
|
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏文本,仅管理员可查看') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
85
admin/src/app/views/diy_form/components/edit-form-mobile.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('内容防重复') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>该组件填写的内容不能与已提交的数据重复。</p>
|
||||||
|
<p>极端情况下可能存在延时导致限制失效。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.unique" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="display-block">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('隐私保护') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>会自动将提交的个人信息做加密展示。</p>
|
||||||
|
<p>适用于公开展示收集的数据且不暴露用户隐私。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.privacyProtection" />
|
||||||
|
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏中间5位数字,仅管理员可查看') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
119
admin/src/app/views/diy_form/components/edit-form-number.vue
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('单位')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.unit" :placeholder="t('请输入单位')" clearable maxlength="5" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('defaultValue') }}</span>
|
||||||
|
<el-tooltip effect="light" :content="t('设置后,默认值会自动填充到输入框,填表人可在此基础上进行修改。')" placement="top">
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.field.default" :placeholder="t('defaultValuePlaceholder')" @keyup="filterDigit($event)" clearable maxlength="18" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<!-- <el-form-item>-->
|
||||||
|
<!-- <template #label>-->
|
||||||
|
<!-- <div class="flex items-center">-->
|
||||||
|
<!-- <span class="mr-[3px]">{{ t('内容防重复') }}</span>-->
|
||||||
|
<!-- <el-tooltip effect="light" placement="top">-->
|
||||||
|
<!-- <template #content>-->
|
||||||
|
<!-- <p>该组件填写的内容不能与已提交的数据重复。</p>-->
|
||||||
|
<!-- <p>极端情况下可能存在延时导致限制失效。</p>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-icon>-->
|
||||||
|
<!-- <QuestionFilled color="#999999" />-->
|
||||||
|
<!-- </el-icon>-->
|
||||||
|
<!-- </el-tooltip>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-switch v-model="diyStore.editComponent.field.unique" />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<!-- <el-form-item class="display-block">-->
|
||||||
|
<!-- <template #label>-->
|
||||||
|
<!-- <div class="flex items-center">-->
|
||||||
|
<!-- <span class="mr-[3px]">{{ t('隐私保护') }}</span>-->
|
||||||
|
<!-- <el-tooltip effect="light" placement="top">-->
|
||||||
|
<!-- <template #content>-->
|
||||||
|
<!-- <p>会自动将提交的个人信息做加密展示。</p>-->
|
||||||
|
<!-- <p>适用于公开展示收集的数据且不暴露用户隐私。</p>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-icon>-->
|
||||||
|
<!-- <QuestionFilled color="#999999" />-->
|
||||||
|
<!-- </el-icon>-->
|
||||||
|
<!-- </el-tooltip>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-switch v-model="diyStore.editComponent.field.privacyProtection" />-->
|
||||||
|
<!-- <div class="text-sm text-gray-400">{{ t('提交后自动隐藏数字,仅管理员可查看') }}</div>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import { filterDigit } from '@/utils/common'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
if(diyStore.value[index].field.default){
|
||||||
|
if (isNaN(diyStore.value[index].field.default) || !regExp.digit.test(diyStore.value[index].field.default)) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('默认值格式输入错误');
|
||||||
|
} else if (diyStore.value[index].field.default < 0) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('默认值不能小于0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正则表达式
|
||||||
|
const regExp: any = {
|
||||||
|
required: /[\S]+/,
|
||||||
|
number: /^\d{0,10}$/,
|
||||||
|
digit: /^\d{0,10}(.?\d{0,2})$/,
|
||||||
|
special: /^\d{0,10}(.?\d{0,3})$/
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
214
admin/src/app/views/diy_form/components/edit-form-radio.vue
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('样式')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.style">
|
||||||
|
<el-radio label="style-1">{{ t('默认') }}</el-radio>
|
||||||
|
<el-radio label="style-2">{{ t('列表') }}</el-radio>
|
||||||
|
<el-radio label="style-3">{{ t('下拉') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('选项')">
|
||||||
|
<div ref="formRadioRef">
|
||||||
|
<div v-for="(option, index) in diyStore.editComponent.options" :key="option.id" class="option-item flex items-center mb-[15px]">
|
||||||
|
<el-input v-model="diyStore.editComponent.options[index].text" class="!w-[215px]" :placeholder="t('optionPlaceholder')" clearable maxlength="30" />
|
||||||
|
<span v-if="diyStore.editComponent.options.length > 1" @click="removeOption(index)" class="cursor-pointer ml-[5px] nc-iconfont nc-icon-shanchu-yuangaizhiV6xx"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]" @click="addOption">添加单个选项</span>
|
||||||
|
<el-popover :visible="visible" placement="bottom" :width="300">
|
||||||
|
<p class="mb-[5px]">批量添加选项</p>
|
||||||
|
<p class="text-[#888] text-[12px] mb-[5px]">可添加多个选项,每个选项之间用英文","隔开</p>
|
||||||
|
<el-input v-model.trim="optionsValue" type="textarea" clearable maxlength="200" show-word-limit />
|
||||||
|
<div class="mt-[10px] text-right">
|
||||||
|
<el-button size="small" text @click="visible = false">取消</el-button>
|
||||||
|
<el-button size="small" type="primary" @click="batchAddOptions">确定</el-button>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<span class="text-primary cursor-pointer" @click="visible = true">批量添加选项</span>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- <el-form-item class="display-block">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('逻辑规则') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>支持选择某个选项后,显示特定的组件。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<el-button plain>{{ t('添加字段显示规则') }}</el-button>
|
||||||
|
<span class="mr-[3px]">1条字段显示规则</span>
|
||||||
|
<span class="text-primary cursor-pointer" @click="">设置</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item> -->
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<!-- <el-form label-width="100px" class="px-[10px]">-->
|
||||||
|
<!-- <el-form-item class="display-block">-->
|
||||||
|
<!-- <template #label>-->
|
||||||
|
<!-- <div class="flex items-center">-->
|
||||||
|
<!-- <span class="mr-[3px]">{{ t('隐私保护') }}</span>-->
|
||||||
|
<!-- <el-tooltip effect="light" placement="top">-->
|
||||||
|
<!-- <template #content>-->
|
||||||
|
<!-- <p>会自动将提交的个人信息做加密展示。</p>-->
|
||||||
|
<!-- <p>适用于公开展示收集的数据且不暴露用户隐私。</p>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-icon>-->
|
||||||
|
<!-- <QuestionFilled color="#999999" />-->
|
||||||
|
<!-- </el-icon>-->
|
||||||
|
<!-- </el-tooltip>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-switch v-model="diyStore.editComponent.field.privacyProtection" />-->
|
||||||
|
<!-- <div class="text-sm text-gray-400">{{ t('提交后自动隐藏内容,仅管理员可查看') }}</div>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
|
||||||
|
<!-- </el-form>-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref, onMounted, nextTick} from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
import Sortable from 'sortablejs'
|
||||||
|
import { range } from 'lodash-es'
|
||||||
|
import { FormInstance, ElMessage } from "element-plus";
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
let pass = true;
|
||||||
|
for (let i = 0; i < diyStore.value[index].options.length; i++) {
|
||||||
|
if (!diyStore.value[index].options[i].text) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('optionPlaceholder');
|
||||||
|
pass = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pass) return res;
|
||||||
|
|
||||||
|
let uniqueOptions = uniqueByKey(diyStore.value[index].options, 'text')
|
||||||
|
|
||||||
|
if (uniqueOptions.length != diyStore.value[index].options.length) {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('存在重复选项,请检查内容')
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
diyStore.editComponent.options.forEach((item: any) => {
|
||||||
|
if (!item.id) item.id = diyStore.generateRandom()
|
||||||
|
})
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const optionsValue = ref()
|
||||||
|
const addOption = () => {
|
||||||
|
diyStore.editComponent.options.push({
|
||||||
|
id: diyStore.generateRandom(),
|
||||||
|
text: '选项' + (diyStore.editComponent.options.length + 1)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeOption = (index:any) => {
|
||||||
|
diyStore.editComponent.options.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const batchAddOptions = () => {
|
||||||
|
if (optionsValue.value.trim()) {
|
||||||
|
const newOptions = optionsValue.value.split(',').map((option: any) => {
|
||||||
|
return {
|
||||||
|
id: diyStore.generateRandom(),
|
||||||
|
text: option.trim()
|
||||||
|
};
|
||||||
|
}).filter((option: any) => option.text !== '');
|
||||||
|
|
||||||
|
// 去除重复的选项
|
||||||
|
const uniqueNewOptions = uniqueByKey(newOptions, 'text');
|
||||||
|
|
||||||
|
// 过滤掉已存在的选项
|
||||||
|
const filteredNewOptions = uniqueNewOptions.filter(newOption =>
|
||||||
|
!diyStore.editComponent.options.some(existingOption => existingOption.text === newOption.text)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果有新的选项,添加到选项列表中
|
||||||
|
if (filteredNewOptions.length > 0) {
|
||||||
|
diyStore.editComponent.options.push(...filteredNewOptions);
|
||||||
|
} else {
|
||||||
|
ElMessage({
|
||||||
|
message: "选项已存在,请重新输入",
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsValue.value = '';
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 数组去重
|
||||||
|
const uniqueByKey = (arr: any, key: any) => {
|
||||||
|
const seen = new Set();
|
||||||
|
return arr.filter((item: any) => {
|
||||||
|
const serializedKey = JSON.stringify(item[key]);
|
||||||
|
return seen.has(serializedKey) ? false : seen.add(serializedKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRadioRef = ref()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
const sortable = Sortable.create(formRadioRef.value, {
|
||||||
|
group: 'option-item',
|
||||||
|
animation: 200,
|
||||||
|
onEnd: event => {
|
||||||
|
const temp = diyStore.editComponent.options[event.oldIndex!]
|
||||||
|
diyStore.editComponent.options.splice(event.oldIndex!, 1)
|
||||||
|
diyStore.editComponent.options.splice(event.newIndex!, 0, temp)
|
||||||
|
sortable.sort(
|
||||||
|
range(diyStore.editComponent.options.length).map(value => {
|
||||||
|
return value.toString()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
113
admin/src/app/views/diy_form/components/edit-form-submit.vue
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<el-form label-width="80px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('按钮位置')" class="display-block">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.btnPosition" @change="btnPositionChangeFn">
|
||||||
|
<el-radio label="follow_content">{{ t('跟随内容') }}</el-radio>
|
||||||
|
<el-radio label="hover_screen_bottom">{{ t('悬浮屏幕底部') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<div class="text-sm text-gray-400 mb-[5px] leading-[1.4]" v-show="diyStore.editComponent.btnPosition == 'follow_content'">{{ t('当表单内容多时,只有滚动页面至最底部才会显示提交按钮') }}</div>
|
||||||
|
<div class="text-sm text-gray-400 mb-[5px]" v-show="diyStore.editComponent.btnPosition == 'hover_screen_bottom'">{{ t('按钮悬浮在屏幕底部,方便填表人快速提交') }}</div>
|
||||||
|
<div class="text-sm text-gray-400 mb-[10px] leading-[1.4]">{{ t('若前端以嵌入形式调用表单,提交按钮组件将不显示,相关业务由该页面负责处理') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('提交按钮') }}</h3>
|
||||||
|
<el-form label-width="80px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('按钮名称')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.submitBtn.text" :placeholder="t('请输入按钮名称')" clearable maxlength="10" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('textColor')">
|
||||||
|
<el-color-picker v-model="diyStore.editComponent.submitBtn.color" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('背景色')">
|
||||||
|
<el-color-picker v-model="diyStore.editComponent.submitBtn.bgColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('重置按钮') }}</h3>
|
||||||
|
<el-form label-width="80px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('carouselSearchTabControl')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.resetBtn.control" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('按钮名称')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.resetBtn.text" :placeholder="t('请输入按钮名称')" clearable maxlength="10" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('textColor')">
|
||||||
|
<el-color-picker v-model="diyStore.editComponent.resetBtn.color" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('背景色')">
|
||||||
|
<el-color-picker v-model="diyStore.editComponent.resetBtn.bgColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('按钮样式') }}</h3>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('topRounded')">
|
||||||
|
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('bottomRounded')">
|
||||||
|
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 单选
|
||||||
|
const btnPositionChangeFn = (e)=>{
|
||||||
|
if(e == 'hover_screen_bottom'){
|
||||||
|
diyStore.editComponent.margin.bottom = 0;
|
||||||
|
diyStore.editComponent.margin.both = 0;
|
||||||
|
diyStore.editComponent.margin.top = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
if (diyStore.value[index].submitBtn.text == '') {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('请输入提交按钮名称');
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (diyStore.value[index].resetBtn.text == '') {
|
||||||
|
res.code = false;
|
||||||
|
res.message = t('请输入重置按钮名称');
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
44
admin/src/app/views/diy_form/components/edit-form-table.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
todo 此处编写表格组件的属性
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('defaultValue') }}</span>
|
||||||
|
<el-tooltip effect="light" :content="t('设置后,默认值会自动填充到输入框,填表人可在此基础上进行修改。')" placement="top">
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.field.default" :placeholder="t('defaultValuePlaceholder')" clearable maxlength="18" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('显示行数')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.rowCount" :placeholder="t('请输入显示行数')" clearable maxlength="2" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
<!--<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item class="display-block">
|
||||||
|
<template #label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-[3px]">{{ t('隐私保护') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>会自动将提交的个人信息做加密展示。</p>
|
||||||
|
<p>适用于公开展示收集的数据且不暴露用户隐私。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-switch v-model="diyStore.editComponent.field.privacyProtection" />
|
||||||
|
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏文本,仅管理员可查看') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
201
admin/src/app/views/diy_form/components/edit-form-time-scope.vue
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('开始时间') }}</h3>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('提示语')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.start.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('默认值')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.start.defaultControl"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.start.defaultControl">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.start.timeWay">
|
||||||
|
<el-radio label="current">{{ t('当天时间') }}</el-radio>
|
||||||
|
<el-radio label="diy">{{ t('指定时间') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.start.defaultControl && diyStore.editComponent.start.timeWay == 'diy'">
|
||||||
|
<el-time-picker v-model="diyStore.editComponent.field.default.start.date" placeholder="请选择开始时间" format="HH:mm" value-format="HH:mm" @change="startTimePickerChange" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('结束时间') }}</h3>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('提示语')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.end.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('默认值')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.end.defaultControl" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.end.defaultControl">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.end.timeWay">
|
||||||
|
<el-radio label="current">{{ t('当天时间') }}</el-radio>
|
||||||
|
<el-radio label="diy">{{ t('指定时间') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.end.defaultControl && diyStore.editComponent.end.timeWay == 'diy'">
|
||||||
|
<el-time-picker :disabled-hours="disabledHours" :disabled-minutes="disabledMinutes" v-model="diyStore.editComponent.field.default.end.date" placeholder="请选择结束时间" format="HH:mm" value-format="HH:mm" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<div class="edit-attr-item-wrap">
|
||||||
|
<h3 class="mb-[10px]">{{ t('文字样式') }}</h3>
|
||||||
|
<el-form label-width="80px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('textFontSize')">
|
||||||
|
<el-slider v-model="diyStore.editComponent.fontSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="12" :max="18" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('textFontWeight')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.fontWeight">
|
||||||
|
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
|
||||||
|
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('textColor')">
|
||||||
|
<el-color-picker v-model="diyStore.editComponent.textColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref,onMounted } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
let starTime = diyStore.value[index].field.default.start.date;
|
||||||
|
let endTime = diyStore.value[index].field.default.end.date;
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
if(diyStore.editComponent.start.timeWay == 'current'){
|
||||||
|
starTime = `${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
if(diyStore.editComponent.end.timeWay == 'current'){
|
||||||
|
endTime = `${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(diyStore.editComponent.start.defaultControl && starTime == ''){
|
||||||
|
res.code = false
|
||||||
|
res.message = "开始时间不能为空"
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
if(diyStore.editComponent.end.defaultControl && endTime == ''){
|
||||||
|
res.code = false
|
||||||
|
res.message = "结束时间不能为空"
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
if(diyStore.editComponent.start.defaultControl && diyStore.editComponent.end.defaultControl && timeInvertSecond(starTime) > timeInvertSecond(endTime)){
|
||||||
|
res.code = false
|
||||||
|
res.message = "开始时间不能大于结束时间"
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const today = new Date();
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
if(!diyStore.editComponent.field.default.start.date){
|
||||||
|
diyStore.editComponent.field.default.start.date = `${hours}:${minutes}`;
|
||||||
|
diyStore.editComponent.field.default.start.timestamp = timeInvertSecond(`${hours}:${minutes}`);
|
||||||
|
}
|
||||||
|
if(!diyStore.editComponent.field.default.end.date){
|
||||||
|
let endDate = new Date();
|
||||||
|
endDate.setHours(today.getHours(), today.getMinutes() + 10, 0, 0); // 在当前时间基础上加 10 分钟
|
||||||
|
const endHours = String(endDate.getHours()).padStart(2, '0');
|
||||||
|
const endMinutes = String(endDate.getMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
diyStore.editComponent.field.default.end.date = `${endHours}:${endMinutes}`;
|
||||||
|
diyStore.editComponent.field.default.end.timestamp = timeInvertSecond(`${endHours}:${endMinutes}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 开始时间选择器
|
||||||
|
const startTimePickerChange = (e) => {
|
||||||
|
diyStore.editComponent.field.default.start.timestamp = timeInvertSecond(e);
|
||||||
|
|
||||||
|
const startTimeArr = e.split(":");
|
||||||
|
const date = new Date();
|
||||||
|
date.setHours(parseInt(startTimeArr[0]), parseInt(startTimeArr[1]), 0, 0);
|
||||||
|
date.setMinutes(date.getMinutes() + 10);
|
||||||
|
const updatedEndTime = `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||||
|
diyStore.editComponent.field.default.end.date = updatedEndTime;
|
||||||
|
diyStore.editComponent.field.default.end.timestamp = timeInvertSecond(updatedEndTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结束时间选择器
|
||||||
|
const endTimePickerChange = (e)=>{
|
||||||
|
diyStore.editComponent.field.default.end.timestamp = timeInvertSecond(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabledHours = () => {
|
||||||
|
let timeArr = diyStore.editComponent.field.default.start.date.split(":")
|
||||||
|
return makeRange(0,timeArr[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabledMinutes = (hour: number) => {
|
||||||
|
let timeArr = diyStore.editComponent.field.default.start.date.split(":")
|
||||||
|
return makeRange(0,timeArr[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeRange = (start: number, end: number) => {
|
||||||
|
const result: number[] = []
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeInvertSecond = (time:any)=>{
|
||||||
|
let arr = time.split(":");
|
||||||
|
let num = 0;
|
||||||
|
if(arr[0]){
|
||||||
|
num += arr[0] * 60 * 60;
|
||||||
|
}
|
||||||
|
if(arr[1]){
|
||||||
|
num += arr[1] * 60;
|
||||||
|
}
|
||||||
|
if(arr[2]){
|
||||||
|
num += arr[2];
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
89
admin/src/app/views/diy_form/components/edit-form-time.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('默认值')">
|
||||||
|
<el-switch v-model="diyStore.editComponent.defaultControl" @change="changeDateDefaultControl" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.defaultControl">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.timeWay">
|
||||||
|
<el-radio label="current">{{ t('当天时间') }}</el-radio>
|
||||||
|
<el-radio label="diy">{{ t('指定时间') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="diyStore.editComponent.defaultControl && diyStore.editComponent.timeWay == 'diy'">
|
||||||
|
<el-time-picker v-model="diyStore.editComponent.field.default" placeholder="请选择时间" format="HH:mm" value-format="HH:mm" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref,watch,onMounted } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始赋值当天时间
|
||||||
|
if (!diyStore.editComponent.field.default) {
|
||||||
|
const today = new Date();
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
diyStore.editComponent.field.default = `${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const changeDateDefaultControl = (val: any) => {
|
||||||
|
if (val) {
|
||||||
|
const today = new Date();
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
diyStore.editComponent.field.default = `${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => diyStore.editComponent.timeWay,
|
||||||
|
(newVal) => {
|
||||||
|
const today = new Date();
|
||||||
|
const hours = String(today.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(today.getMinutes()).padStart(2, '0');
|
||||||
|
diyStore.editComponent.field.default = `${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
42
admin/src/app/views/diy_form/components/edit-form-video.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]">
|
||||||
|
<el-form-item :label="t('上传方式')">
|
||||||
|
<el-radio-group v-model="diyStore.editComponent.uploadMode">
|
||||||
|
<el-radio label="shoot_and_album">{{ t('拍摄和相册') }}</el-radio>
|
||||||
|
<el-radio label="shoot_only">{{ t('只允许拍摄') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段内容设置 -->
|
||||||
|
<slot name="field"></slot>
|
||||||
|
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
|
||||||
|
<el-form-item :label="t('formPlaceholder')">
|
||||||
|
<el-input v-model.trim="diyStore.editComponent.placeholder" :placeholder="t('formPlaceholderTips')" clearable maxlength="15" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 表单组件 其他设置 -->
|
||||||
|
<slot name="other"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 样式 -->
|
||||||
|
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||||
|
|
||||||
|
<!-- 表单组件 字段样式 -->
|
||||||
|
<slot name="style-field"></slot>
|
||||||
|
|
||||||
|
<!-- 组件样式 -->
|
||||||
|
<slot name="style"></slot>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import useDiyStore from '@/stores/modules/diy'
|
||||||
|
|
||||||
|
const diyStore = useDiyStore()
|
||||||
|
diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
|
||||||
|
|
||||||
|
// 组件验证
|
||||||
|
diyStore.editComponent.verify = (index: number) => {
|
||||||
|
const res = { code: true, message: '' }
|
||||||
|
// todo 只需要考虑该组件自身的验证
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
defineExpose({})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
144
admin/src/app/views/diy_form/components/form-spread-popup.vue
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog v-model="showDialog" :title="t('表单推广')" width="500px" :destroy-on-close="true">
|
||||||
|
|
||||||
|
<el-tabs v-model="channel" class="mb-[10px]">
|
||||||
|
<el-tab-pane label="H5" name="h5"></el-tab-pane>
|
||||||
|
<el-tab-pane label="微信小程序" name="weapp"></el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<div class="promote-flex flex" v-if="channel == 'h5'">
|
||||||
|
<div class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]">
|
||||||
|
<el-image :src="wapImage" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[20px] flex-1">
|
||||||
|
<div class="mb-[10px]">{{ t('推广链接') }}</div>
|
||||||
|
<el-input class="mb-[10px]" readonly :value="wapPreview">
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="copyEvent(wapPreview)">{{ t('copy') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<a class="text-primary" :href="wapImage" download>{{ t('下载二维码') }}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="promote-flex flex" v-if="channel == 'weapp'">
|
||||||
|
<div class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]">
|
||||||
|
<el-image :src="img(weappData.path)" v-if="weappData.path" class="w-[150px] h-[150px]">
|
||||||
|
<template #error>
|
||||||
|
<div class="w-[150px] h-[150px] text-[14px] text-center leading-[150px]">配置失败</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<div v-else class="w-[150px] h-[150px] text-[14px] text-center leading-[150px]">配置失败</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[20px] flex-1">
|
||||||
|
<a class="text-primary" :href="img(weappData.path)" target="_blank" download>{{ t('下载二维码') }}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref, reactive, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
import { getUrl } from '@/app/api/sys'
|
||||||
|
import { getDiyFormQrcode } from '@/app/api/diy_form'
|
||||||
|
import { img } from '@/utils/common'
|
||||||
|
|
||||||
|
const form: any = reactive({})
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const channel = ref('h5')
|
||||||
|
const wapUrl = ref('')
|
||||||
|
const wapDomain = ref('')
|
||||||
|
const wapImage = ref('')
|
||||||
|
const wapPreview = ref('')
|
||||||
|
const page = ref('')
|
||||||
|
const weappData = reactive({
|
||||||
|
path: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
getUrl().then((res: any) => {
|
||||||
|
wapUrl.value = res.data.wap_url
|
||||||
|
|
||||||
|
// 生产模式禁止
|
||||||
|
if (import.meta.env.MODE == 'production') return
|
||||||
|
|
||||||
|
wapDomain.value = res.data.wap_domain
|
||||||
|
|
||||||
|
// env文件配置过wap域名
|
||||||
|
if (wapDomain.value) {
|
||||||
|
wapUrl.value = wapDomain.value + '/wap'
|
||||||
|
}
|
||||||
|
|
||||||
|
const wapDomainStorage = storage.get('wap_domain')
|
||||||
|
if (wapDomainStorage) {
|
||||||
|
wapUrl.value = wapDomainStorage
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadQrcode = () => {
|
||||||
|
wapPreview.value = `${wapUrl.value}${page.value}`
|
||||||
|
// errorCorrectionLevel:密度容错率L(低)H(高)
|
||||||
|
QRCode.toDataURL(wapPreview.value, { errorCorrectionLevel: 'L', margin: 0, width: 120 }).then(url => {
|
||||||
|
wapImage.value = url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const show = (data: any) => {
|
||||||
|
channel.value = 'h5';
|
||||||
|
Object.assign(form, data)
|
||||||
|
page.value = `/app/pages/index/diy_form?form_id=${form.form_id}`
|
||||||
|
|
||||||
|
loadQrcode()
|
||||||
|
getDiyFormQrcodeFn();
|
||||||
|
showDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDiyFormQrcodeFn = ()=>{
|
||||||
|
getDiyFormQrcode({
|
||||||
|
form_id:form.form_id
|
||||||
|
}).then((res:any)=>{
|
||||||
|
if(res.data) {
|
||||||
|
weappData.path = res.data.path;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制
|
||||||
|
const { copy, isSupported, copied } = useClipboard()
|
||||||
|
const copyEvent = (text: string) => {
|
||||||
|
if (!isSupported.value) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('notSupportCopy'),
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
copy(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(copied, () => {
|
||||||
|
if (copied.value) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('copySuccess'),
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
showDialog,
|
||||||
|
show
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
435
admin/src/app/views/diy_form/components/form-submit-popup.vue
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog v-model="showDialog" :title="t('提交成功页')" width="850px" :close-on-press-escape="true" :destroy-on-close="true" :close-on-click-modal="false">
|
||||||
|
|
||||||
|
<div class="flex flex-1 mt-[24px] mx-[24px] mb-0">
|
||||||
|
<div class="preview-wrap">
|
||||||
|
<div class="absolute z-1 left-0 top-0">
|
||||||
|
<img src="@/app/assets/images/diy_form/mobile_tabbar.png" class="w-[324px]" />
|
||||||
|
</div>
|
||||||
|
<div class="absolute z-1 left-0 bottom-0">
|
||||||
|
<img src="@/app/assets/images/diy_form/mobile_bottom.png" class="w-[324px]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page-wrap">
|
||||||
|
<div class="px-[13px] flex flex-col items-center flex-1">
|
||||||
|
<div class="flex items-center justify-center w-[48px] h-[48px] text-[40px] p-[4px] mt-[32px] mb-[16px] mx-auto rounded-[50%]">
|
||||||
|
<el-icon><SuccessFilled color="#20bf64" /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="record-name">
|
||||||
|
<span class="text-[#1E1E1E] font-bold text-[24px]" v-if="formData.tips_type == 'default'">填写成功</span>
|
||||||
|
<span class="text-[#1E1E1E] font-bold text-[16px]" v-else-if="formData.tips_type == 'diy'">{{ formData.tips_text ? formData.tips_text : '填写成功' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="to-detail">
|
||||||
|
<div class="text-[14px] mt-[16px] py-[4px] px[8px] text-[#576b95]">查看填写详情</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="relative pt-[8px] pb-[48px] h-[112px]">
|
||||||
|
<div v-if="formData.success_after_action.finish" class="!mt-[16px] rounded-[3px] mx-auto text-[15px] w-[100px] min-w-[160px] h-[32px] leading-[32px] text-center max-w-[274px] truncate bg-[#20bf64] text-[#ffffff]">
|
||||||
|
<div class="text-[15px]">完成</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="formData.success_after_action.goback" class="!mt-[16px] rounded-[3px] mx-auto text-[15px] w-[100px] min-w-[160px] h-[32px] leading-[32px] text-center max-w-[274px] truncate bg-[#f2f2f2] text-[#353535]">
|
||||||
|
<div class="text-[14px]">返回</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 核销凭证 todo 后续完善 -->
|
||||||
|
<!-- <div class="page-wrap verify-voucher-wrap" style="display:none;">-->
|
||||||
|
|
||||||
|
<!-- <div class="tips-wrap">感谢你的填写,以下是你的核销凭证</div>-->
|
||||||
|
<!-- <div class="qrcode-wrap">-->
|
||||||
|
<!-- <div class="text-[14px] text-[#333]">请妥善保存你的核销凭证</div>-->
|
||||||
|
<!-- <div class="text-[20px] font-bold text-[#333] my-[10px]">现场出示凭证</div>-->
|
||||||
|
<!-- <el-image class="w-[180px]" :src="wapImage" />-->
|
||||||
|
<!-- <div class="text-primary mt-[10px]">保存凭证</div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="relative pt-[8px] pb-[48px] h-[112px]">-->
|
||||||
|
<!-- <div class="!mt-[16px] rounded-[3px] mx-auto text-[15px] w-[100px] min-w-[160px] h-[32px] leading-[32px] text-center max-w-[274px] truncate bg-[#20bf64] text-[#ffffff]">-->
|
||||||
|
<!-- <div class="text-[15px]">返回二维码</div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="!mt-[16px] rounded-[3px] mx-auto text-[15px] w-[100px] min-w-[160px] h-[32px] leading-[32px] text-center max-w-[274px] truncate bg-[#fff] text-[#353535]">-->
|
||||||
|
<!-- <div class="text-[14px]">完成</div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="text-[14px] mt-[16px] py-[4px] px[8px] text-[#576b95]">查看填写详情</div>-->
|
||||||
|
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="item-wrap">
|
||||||
|
<div class="text-[16px] h-[24px] font-bold text-[#262626] mb-[16px] w-[140px] mr-[32px] flex-shrink-0">填表人提交后</div>
|
||||||
|
<el-radio-group v-model="formData.submit_after_action" class="!block">
|
||||||
|
<el-radio label="text" class="!flex">
|
||||||
|
<span class="mr-[3px]">{{ t('显示文字信息') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>{{ t('提交后页面显示文字信息。') }}</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio>
|
||||||
|
<!-- todo 后续完善 -->
|
||||||
|
<!-- <el-radio label="voucher" class="!flex">-->
|
||||||
|
<!-- <span class="mr-[3px]">{{ t('获取核销凭证') }}</span>-->
|
||||||
|
<!-- <el-tooltip effect="light" placement="top">-->
|
||||||
|
<!-- <template #content>-->
|
||||||
|
<!-- <p>{{ t('提交后页面会将提交的表单记录内容生成二维码并展示,可选择设置两种不同的二维码内容。适合核销、数据录入等场景。') }}</p>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <el-icon>-->
|
||||||
|
<!-- <QuestionFilled color="#999999" />-->
|
||||||
|
<!-- </el-icon>-->
|
||||||
|
<!-- </el-tooltip>-->
|
||||||
|
<!-- </el-radio>-->
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-wrap" v-if="formData.submit_after_action == 'text'">
|
||||||
|
<div class="text-[16px] h-[24px] font-bold text-[#262626] mb-[16px] w-[140px] mr-[32px] flex-shrink-0">{{ t('提示文字') }}</div>
|
||||||
|
<div>
|
||||||
|
<el-radio-group v-model="formData.tips_type" class="!block">
|
||||||
|
<el-radio label="default" class="!block">
|
||||||
|
<span class="mr-[3px]">{{ t('默认提示') }}</span>
|
||||||
|
<span class="!text-[#999] text-[12px] ml-[8px]">{{ t('将显示: 填写成功') }}</span>
|
||||||
|
</el-radio>
|
||||||
|
<el-radio label="diy" class="!block">{{ t('自定义提示') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<el-input v-if="formData.tips_type == 'diy'" v-model.trim="formData.tips_text" :placeholder="t('感谢你的填写')" class="w-[350px]" maxlength="30" clearable show-word-limit />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 核销凭证 todo 后续完善 -->
|
||||||
|
<template v-else-if="formData.submit_after_action == 'voucher'">
|
||||||
|
|
||||||
|
<div class="item-wrap">
|
||||||
|
<div class="text-[16px] h-[24px] font-bold text-[#262626] mb-[16px] w-[140px] mr-[32px] flex-shrink-0">{{ t('凭证有效期') }}</div>
|
||||||
|
<div>
|
||||||
|
<el-radio-group v-model="formData.time_limit_type" class="!block">
|
||||||
|
<el-radio label="no_limit" class="!block">{{ t('不限制') }}</el-radio>
|
||||||
|
<el-radio label="specify_time" class="!block">
|
||||||
|
<span class="mr-[3px]">{{ t('设置固定有效期') }}</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p>{{ t('每条记录的凭证有效期都是一样的,例如:会议凭证可设置有效期为会议举行时间。') }}</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio>
|
||||||
|
<el-radio label="submission_time" class="!block">
|
||||||
|
<span class="mr-[3px]">按提交时间设置有效期</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p class="w-[250px]">每条记录的凭证有效期按照提交时间来计算,例如:优惠凭证的有效期可以设置为领取后3天内有效。</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<el-date-picker v-if="formData.time_limit_type == 'specify_time'" v-model="formData.validity_time" type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||||
|
<div class="flex items-center mt-[5px]" v-if="formData.time_limit_type == 'submission_time'">
|
||||||
|
<span>提交记录后</span>
|
||||||
|
<!-- <div class="flex items-center px-[5px]">-->
|
||||||
|
<!-- v-model.trim="formData.length"-->
|
||||||
|
<el-input v-model.trim="formData.submission_time_value" @keyup="filterNumber($event)" size="small" clearable class="!w-[100px] px-[5px]" maxlength="3" />
|
||||||
|
<el-select v-model="formData.timeUnit" clearable class="!w-[100px] pr-[5px]" size="small">
|
||||||
|
<el-option v-for="(item, index) in validityOptions" :key="item.value" :label="item.text" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
<span>有效</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-wrap">
|
||||||
|
<div class="text-[16px] h-[24px] font-bold text-[#262626] mb-[16px] w-[140px] mr-[32px] flex-shrink-0">凭证样式</div>
|
||||||
|
<div>
|
||||||
|
<el-form-item :label="t('码上方标题')">
|
||||||
|
<!-- v-model.trim="formData.active_name"-->
|
||||||
|
<el-input clearable :placeholder="t('请妥善保存你的核销凭证')" class="input-width" :maxlength="20" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('码上方内容')">
|
||||||
|
<el-input clearable :placeholder="t('请输入码上方内容')" class="input-width" :maxlength="20" />
|
||||||
|
<div>
|
||||||
|
<span class="text-primary cursor-pointer mr-[10px]">添加换行符</span>
|
||||||
|
<span class="text-primary cursor-pointer">添加字段</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('码下方内容')">
|
||||||
|
<div class="block">
|
||||||
|
<el-checkbox class="!block" label="展示提交记录时间" value="" />
|
||||||
|
|
||||||
|
<el-checkbox class="!block !h-[20px]" label="展示当前时间" value="" />
|
||||||
|
<div class="text-[#999] ml-[22px]">会以秒进行跳动,可起到防作假的功能</div>
|
||||||
|
|
||||||
|
<el-checkbox class="!block" label="展示提示文字" value="" />
|
||||||
|
<el-input class="ml-[22px]" :rows="4" type="textarea" placeholder="感谢你的填写" maxlength="100" />
|
||||||
|
|
||||||
|
<el-checkbox class="!block" label="展示凭证截止时间" value="" />
|
||||||
|
<el-checkbox class="!block" label="支持填表人保存凭证" value="" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- todo 后续完善 -->
|
||||||
|
<div class="item-wrap">
|
||||||
|
<div class="text-[16px] h-[24px] font-bold text-[#262626] mb-[16px] w-[140px] mr-[32px] flex-shrink-0">
|
||||||
|
<span>后续操作按钮</span>
|
||||||
|
<!-- <p class="text-[12px] text-[#999] mt-[4px] font-normal">最多选择2个</p>-->
|
||||||
|
</div>
|
||||||
|
<div class="content-list-wrap">
|
||||||
|
<!-- <el-checkbox-group :min="1" :max="2">-->
|
||||||
|
<!-- <el-checkbox v-model="formData.success_after_action.share" label="转发填写内容" value="share">-->
|
||||||
|
<!-- <div class="text-[#333]">转发填写内容</div>-->
|
||||||
|
<!-- </el-checkbox>-->
|
||||||
|
<!-- <p class="text-[#999] text-[12px] pl-[24px] mt-[4px]">提交表单后,可转发给微信好友查看。支持按钮文案自定义,提醒填表人转发给特定人员查看</p>-->
|
||||||
|
|
||||||
|
<el-checkbox v-model="formData.success_after_action.finish" label="完成" value="finish">
|
||||||
|
<div class="text-[#333]">完成</div>
|
||||||
|
</el-checkbox>
|
||||||
|
<p class="text-[#999] text-[12px] pl-[24px] mt-[4px]">点击后进入首页</p>
|
||||||
|
|
||||||
|
<el-checkbox v-model="formData.success_after_action.goback" label="返回" value="goback">
|
||||||
|
<div class="text-[#333]">返回</div>
|
||||||
|
</el-checkbox>
|
||||||
|
<p class="text-[#999] text-[12px] pl-[24px] mt-[4px]">点击后返回表单</p>
|
||||||
|
<!-- </el-checkbox-group>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="confirm">{{ t('保存') }}</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
import { filterNumber } from '@/utils/common'
|
||||||
|
import { getUrl } from '@/app/api/sys'
|
||||||
|
import { getFormSubmitConfig,editDiyFormSubmitConfig } from '@/app/api/diy_form'
|
||||||
|
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const repeat = ref(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单数据
|
||||||
|
*/
|
||||||
|
const initialFormData = {
|
||||||
|
id: 0,
|
||||||
|
form_id: 0,
|
||||||
|
submit_after_action: 'text', // 填表人提交后操作,text:文字信息,voucher:核销凭证
|
||||||
|
tips_type: 'default', // 提示内容类型,default:默认提示,diy:自定义提示
|
||||||
|
tips_text: '', // 自定义提示内容
|
||||||
|
time_limit_type: 'no_limit', // 核销凭证有效期限制类型,no_limit:不限制,specify_time:指定固定开始结束时间,submission_time:按提交时间设置有效期
|
||||||
|
// 核销凭证时间限制规则,json格式 todo 结构待定,后续完善
|
||||||
|
time_limit_rule: {
|
||||||
|
validity_time: [], // 指定固定开始结束时间
|
||||||
|
submission_time_value: '', // 按提交时间设置有效期
|
||||||
|
timeUnit: 'day', // 提交时间单位
|
||||||
|
},
|
||||||
|
// 核销凭证内容,json格式 todo 结构待定,后续完善
|
||||||
|
voucher_content_rule: {},
|
||||||
|
// 填写成功后续操作
|
||||||
|
success_after_action: {
|
||||||
|
share: false, // 转发填写内容
|
||||||
|
finish: true, // 完成
|
||||||
|
goback: true, // 返回
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||||
|
|
||||||
|
const wapUrl = ref('')
|
||||||
|
const wapDomain = ref('')
|
||||||
|
const wapImage = ref('')
|
||||||
|
const wapPreview = ref('')
|
||||||
|
const page = ref('')
|
||||||
|
|
||||||
|
// 核销凭证有效期
|
||||||
|
const validityOptions = reactive([
|
||||||
|
{
|
||||||
|
text:'天',
|
||||||
|
value:'day'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text:'周',
|
||||||
|
value:'week'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text:'月',
|
||||||
|
value:'month'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text:'年',
|
||||||
|
value:'year'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text:'分钟',
|
||||||
|
value:'minutes'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// getUrl().then((res: any) => {
|
||||||
|
// wapUrl.value = res.data.wap_url
|
||||||
|
//
|
||||||
|
// // 生产模式禁止
|
||||||
|
// if (import.meta.env.MODE == 'production') return
|
||||||
|
//
|
||||||
|
// wapDomain.value = res.data.wap_domain
|
||||||
|
//
|
||||||
|
// // env文件配置过wap域名
|
||||||
|
// if (wapDomain.value) {
|
||||||
|
// wapUrl.value = wapDomain.value + '/wap'
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const wapDomainStorage = storage.get('wap_domain')
|
||||||
|
// if (wapDomainStorage) {
|
||||||
|
// wapUrl.value = wapDomainStorage
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
const loadQrcode = () => {
|
||||||
|
wapPreview.value = `${wapUrl.value}${page.value}`
|
||||||
|
// errorCorrectionLevel:密度容错率L(低)H(高)
|
||||||
|
QRCode.toDataURL(wapPreview.value, { errorCorrectionLevel: 'L', margin: 0, width: 120 }).then(url => {
|
||||||
|
wapImage.value = url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['complete'])
|
||||||
|
|
||||||
|
const setFormData = async (row: any = null) => {
|
||||||
|
Object.assign(formData, initialFormData)
|
||||||
|
if (row) {
|
||||||
|
const data = await (await getFormSubmitConfig(row.form_id)).data
|
||||||
|
if (data) {
|
||||||
|
Object.keys(formData).forEach((key: string) => {
|
||||||
|
if (data[key] != undefined) formData[key] = data[key]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// todo 靠后完善
|
||||||
|
// page.value = `/app/pages/index/diy_form?form_id=${formData.form_id}`
|
||||||
|
// loadQrcode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认
|
||||||
|
*/
|
||||||
|
const confirm = () => {
|
||||||
|
if(formData.tips_type == 'diy' && !formData.tips_text){
|
||||||
|
ElMessage.error('提示不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeat.value) return;
|
||||||
|
repeat.value = true
|
||||||
|
|
||||||
|
const data = formData
|
||||||
|
|
||||||
|
editDiyFormSubmitConfig(data).then(res => {
|
||||||
|
repeat.value = false
|
||||||
|
showDialog.value = false
|
||||||
|
emit('complete')
|
||||||
|
}).catch(err => {
|
||||||
|
repeat.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
showDialog,
|
||||||
|
setFormData
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.preview-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 324px;
|
||||||
|
min-height: 555px;
|
||||||
|
padding: 82px 12px 20px;
|
||||||
|
background-size: 100%;
|
||||||
|
background-repeat: repeat-y;
|
||||||
|
background-image: url(../../../../app/assets/images/diy_form/mobile_line.png);
|
||||||
|
border-radius: 38px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: #fff !important;
|
||||||
|
margin-right: 24px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.page-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 548px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verify-voucher-wrap {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
.tips-wrap{
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 21px;
|
||||||
|
color: rgba(0,0,0,.65);
|
||||||
|
margin: 20px 10px;
|
||||||
|
}
|
||||||
|
.qrcode-wrap{
|
||||||
|
border-radius: 12px;
|
||||||
|
margin: 0 20px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-wrap {
|
||||||
|
padding: 20px 24px 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
width: calc(100% - 48px);
|
||||||
|
background-color: hsla(210, 8%, 51%, .13);
|
||||||
|
position: absolute;
|
||||||
|
left: 24px;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
341
admin/src/app/views/diy_form/components/form-write-popup.vue
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="showDialog" :title="t('填写设置')" width="600px" class="diy-dialog-wrap" :close-on-press-escape="true" :destroy-on-close="true" :close-on-click-modal="false">
|
||||||
|
|
||||||
|
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||||
|
|
||||||
|
<!-- <el-form-item :label="t('填写方式')">-->
|
||||||
|
<!-- <el-radio-group v-model="formData.write_way">-->
|
||||||
|
<!-- <el-radio label="no_limit">{{t('不限制')}}</el-radio>-->
|
||||||
|
<!-- <el-radio label="scan">{{t('仅限扫一扫')}}</el-radio>-->
|
||||||
|
<!-- <el-radio label="url">{{t('仅限链接进入')}}</el-radio>-->
|
||||||
|
<!-- </el-radio-group>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
|
||||||
|
<el-form-item :label="t('joinMemberType')">
|
||||||
|
<el-radio-group v-model="formData.join_member_type">
|
||||||
|
<el-radio label="all_member">{{t('allMember')}}</el-radio>
|
||||||
|
<el-radio label="selected_member_level">{{t('selectedMemberLevel')}}</el-radio>
|
||||||
|
<el-radio label="selected_member_label">{{t('selectedMemberLabel')}}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 会员标签 -->
|
||||||
|
<el-form-item :label="t('memberLabel')" prop="label_ids" v-if="formData.join_member_type=='selected_member_label'">
|
||||||
|
<el-select v-model="formData.label_ids" clearable multiple :placeholder="t('memberLabelPlaceholder')" class="input-width">
|
||||||
|
<el-option :label="item['label_name']" :value="item['label_id']" v-for="(item, index) in labelSelectData" :key="index" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- 会员等级 -->
|
||||||
|
<el-form-item :label="t('memberLevel')" prop="level_ids" v-if="formData.join_member_type=='selected_member_level'">
|
||||||
|
<el-select v-model="formData.level_ids" clearable multiple :placeholder="t('memberLevelPlaceholder')" class="input-width">
|
||||||
|
<el-option :label="item['level_name']" :value="item['level_id']" v-for="(item, index) in levelSelectData" :key="index" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="t('每人可填写次数')" :class="{ '!mb-[5px]' : formData.member_write_type == 'diy' }">
|
||||||
|
<el-radio-group v-model="formData.member_write_type">
|
||||||
|
<el-radio label="no_limit">{{t('不限制')}}</el-radio>
|
||||||
|
<el-radio label="diy">{{t('自定义')}}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label=" " v-if="formData.member_write_type == 'diy'" prop="member_write_rule">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>每</span>
|
||||||
|
<el-input v-model.trim="formData.member_write_rule.time_value" @keyup="filterNumber($event)" size="small" class="!w-[50px] px-[5px]" maxlength="3" />
|
||||||
|
<el-select v-model="formData.member_write_rule.time_unit" class="!w-[60px] pr-[5px]" size="small">
|
||||||
|
<el-option v-for="(item, index) in validityOptions" :key="item.value" :label="item.text" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
<span>可填写</span>
|
||||||
|
<el-input v-model.trim="formData.member_write_rule.num" @keyup="filterNumber($event)" size="small" class="!w-[50px] px-[5px]" maxlength="3" />
|
||||||
|
<span>次</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="t('表单可填写总数')" :class="{ '!mb-[5px]' : formData.form_write_type == 'diy' }">
|
||||||
|
<el-radio-group v-model="formData.form_write_type">
|
||||||
|
<el-radio label="no_limit">{{t('不限制')}}</el-radio>
|
||||||
|
<el-radio label="diy">{{t('自定义')}}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label=" " v-if="formData.form_write_type == 'diy'" prop="form_write_rule">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>每</span>
|
||||||
|
<el-input v-model.trim="formData.form_write_rule.time_value" @keyup="filterNumber($event)" size="small" class="!w-[50px] px-[5px]" maxlength="3" />
|
||||||
|
<el-select v-model="formData.form_write_rule.time_unit" class="!w-[60px] pr-[5px]" size="small">
|
||||||
|
<el-option v-for="(item, index) in validityOptions" :key="item.value" :label="item.text" :value="item.value"></el-option>
|
||||||
|
</el-select>
|
||||||
|
<span>可填写</span>
|
||||||
|
<el-input v-model.trim="formData.form_write_rule.num" @keyup="filterNumber($event)" size="small" class="!w-[50px] px-[5px]" maxlength="3" />
|
||||||
|
<span class="mr-[5px]">次</span>
|
||||||
|
<el-tooltip effect="light" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<p class="w-[250px]">{{ t('填写限制的校验在极端情况下可能存在延时,从而导致限制失效,不建议商品限时抢购等场景使用该功能') }}</p>
|
||||||
|
</template>
|
||||||
|
<el-icon>
|
||||||
|
<QuestionFilled color="#999999" />
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="t('可填写时间段')" prop="time_limit_rule">
|
||||||
|
<el-radio-group v-model="formData.time_limit_type">
|
||||||
|
<el-radio label="no_limit">{{t('不限制')}}</el-radio>
|
||||||
|
<el-radio label="specify_time">{{t('设置开始/停止时间')}}</el-radio>
|
||||||
|
<el-radio label="open_day_time">{{t('设置每日开启时间')}}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<el-date-picker v-if="formData.time_limit_type == 'specify_time'" v-model="formData.time_limit_rule.specify_time" type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||||
|
<div class="flex items-center mt-[5px]" v-if="formData.time_limit_type == 'open_day_time'">
|
||||||
|
<span class="mr-[5px]">每天</span>
|
||||||
|
<el-time-picker class="!w-[180px]" v-model="formData.time_limit_rule.open_day_time" format="HH:mm" value-format="HH:mm" is-range range-separator="-" start-placeholder="开始时间" end-placeholder="结束时间" />
|
||||||
|
<span class="ml-[5px]">可填写</span>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!-- <el-form-item :label="t('允许修改内容')" class="display-block">-->
|
||||||
|
<!-- <el-switch v-model="formData.is_allow_update_content" :active-value="1" :inactive-value="0" />-->
|
||||||
|
<!-- <div class="text-sm text-gray-400">{{ t('开启后,填表人可以修改自己填写的内容。') }}</div>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
|
||||||
|
<!-- <el-form-item :label="t('填写须知')">-->
|
||||||
|
<!-- <el-input v-model.trim="formData.write_instruction" :placeholder="t('请输入填写须知')" type="textarea" maxlength="500" show-word-limit rows="5" class="w-[400px]" clearable />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, computed } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import type { FormInstance } from 'element-plus'
|
||||||
|
import { filterNumber } from '@/utils/common'
|
||||||
|
import {getMemberLabelAll,getMemberLevelAll } from '@/app/api/member'
|
||||||
|
import { getFormWriteConfig,editDiyFormWriteConfig } from '@/app/api/diy_form'
|
||||||
|
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单数据
|
||||||
|
*/
|
||||||
|
const initialFormData = {
|
||||||
|
id: 0,
|
||||||
|
form_id: 0, // 万能表单id
|
||||||
|
write_way: 'no_limit', // 填写方式,no_limit:不限制,scan:仅限微信扫一扫,url:仅限链接进入
|
||||||
|
join_member_type: 'all_member', // 参与会员,all_member:所有会员参与,selected_member_level:指定会员等级,selected_member_label:指定会员标签
|
||||||
|
level_ids: [], // 会员等级id集合
|
||||||
|
label_ids: [], // 会员标签id集合
|
||||||
|
member_write_type: 'no_limit', // 每人可填写次数,no_limit:不限制,diy:自定义
|
||||||
|
// 每人可填写次数自定义规则
|
||||||
|
member_write_rule: {
|
||||||
|
time_value: 1, // 时间
|
||||||
|
time_unit: 'day', // 时间单位
|
||||||
|
num: 1 // 可填写次数
|
||||||
|
},
|
||||||
|
form_write_type: 'no_limit', // 表单可填写数量,no_limit:不限制,diy:自定义
|
||||||
|
// 表单可填写总数自定义规则
|
||||||
|
form_write_rule: {
|
||||||
|
time_value: 1, // 时间
|
||||||
|
time_unit: 'day', // 时间单位
|
||||||
|
num: 1 // 可填写次数
|
||||||
|
},
|
||||||
|
time_limit_type: 'no_limit', // 填写时间限制类型,no_limit:不限制,specify_time:指定开始结束时间,open_day_time:设置每日开启时间
|
||||||
|
// 填写时间限制规则
|
||||||
|
time_limit_rule: {
|
||||||
|
specify_time: [], // 指定开始结束时间
|
||||||
|
open_day_time: [], // 设置每日开启时间
|
||||||
|
},
|
||||||
|
is_allow_update_content: 0, // 是否允许修改自己填写的内容,0:否,1:是
|
||||||
|
write_instruction: '', // 表单填写须知
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const formRules = computed(() => {
|
||||||
|
return {
|
||||||
|
label_ids: [
|
||||||
|
{ required: true, message: t('labelTips'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
level_ids: [
|
||||||
|
{ required: true, message: t('levelTips'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
member_write_rule: [
|
||||||
|
{
|
||||||
|
validator: (rule: any, value: string, callback: any) => {
|
||||||
|
let unit = ''
|
||||||
|
validityOptions.forEach((item,index)=>{
|
||||||
|
if(item.value == value.time_unit){
|
||||||
|
unit = item.text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if(formData.member_write_type == 'diy'){
|
||||||
|
if(!value.time_value){
|
||||||
|
callback(new Error(`${unit}数不能为空`))
|
||||||
|
}else if(!value.num){
|
||||||
|
callback(new Error(`次数不能为空`))
|
||||||
|
}else{
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
form_write_rule: [
|
||||||
|
{
|
||||||
|
validator: (rule: any, value: string, callback: any) => {
|
||||||
|
let unit = ''
|
||||||
|
validityOptions.forEach((item,index)=>{
|
||||||
|
if(item.value == value.time_unit){
|
||||||
|
unit = item.text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if(formData.member_write_type == 'diy'){
|
||||||
|
if(!value.time_value){
|
||||||
|
callback(new Error(`${unit}数不能为空`))
|
||||||
|
}else if(!value.num){
|
||||||
|
callback(new Error(`次数不能为空`))
|
||||||
|
}else{
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
time_limit_rule: [
|
||||||
|
{
|
||||||
|
validator: (rule: any, value: string, callback: any) => {
|
||||||
|
if (formData.time_limit_type == 'specify_time' && (!value.specify_time || !value.specify_time.length)) {
|
||||||
|
callback(new Error('开始/停止时间不能为空'))
|
||||||
|
} else if (formData.time_limit_type == 'open_day_time' && (!value.open_day_time || !value.open_day_time.length)) {
|
||||||
|
callback(new Error('开启时间不能为空'))
|
||||||
|
} else if (formData.time_limit_type == 'open_day_time' && value.open_day_time && value.open_day_time.length) {
|
||||||
|
if (value.open_day_time[0] == value.open_day_time[1]) {
|
||||||
|
callback(new Error('开始时间不能等于结束时间'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: ['blur', 'change']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const levelSelectData = ref([])
|
||||||
|
const labelSelectData = ref([])
|
||||||
|
|
||||||
|
// 获取全部标签
|
||||||
|
getMemberLabelAll().then(({ data }) => {
|
||||||
|
labelSelectData.value = data
|
||||||
|
})
|
||||||
|
|
||||||
|
getMemberLevelAll().then(({ data }) => {
|
||||||
|
levelSelectData.value = data
|
||||||
|
})
|
||||||
|
|
||||||
|
const validityOptions = reactive([
|
||||||
|
{
|
||||||
|
text:'天',
|
||||||
|
value:'day'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text:'周',
|
||||||
|
value:'week'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text:'月',
|
||||||
|
value:'month'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text:'年',
|
||||||
|
value:'year'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const emit = defineEmits(['complete'])
|
||||||
|
|
||||||
|
const setFormData = async (row: any = null) => {
|
||||||
|
Object.assign(formData, initialFormData)
|
||||||
|
loading.value = true
|
||||||
|
if (row) {
|
||||||
|
const data = await (await getFormWriteConfig(row.form_id)).data
|
||||||
|
if (data && Object.keys(data).length) {
|
||||||
|
Object.keys(formData).forEach((key: string) => {
|
||||||
|
if (data[key] != undefined) formData[key] = data[key]
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
formData.form_id = row.form_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认
|
||||||
|
* @param formEl
|
||||||
|
*/
|
||||||
|
const confirm = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (loading.value || !formEl) return
|
||||||
|
await formEl.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
const data = formData
|
||||||
|
|
||||||
|
editDiyFormWriteConfig(data).then(res => {
|
||||||
|
loading.value = false
|
||||||
|
showDialog.value = false
|
||||||
|
emit('complete')
|
||||||
|
}).catch(err => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterSpecial = (event:any) => {
|
||||||
|
event.target.value = event.target.value.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '')
|
||||||
|
event.target.value = event.target.value.replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
showDialog,
|
||||||
|
setFormData
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
<style lang="scss">
|
||||||
|
.diy-dialog-wrap .el-form-item__label{
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-block {
|
||||||
|
.el-form-item__content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
1021
admin/src/app/views/diy_form/edit.vue
Normal file
582
admin/src/app/views/diy_form/list.vue
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
<template>
|
||||||
|
<div class="main-container">
|
||||||
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
|
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-page-title">{{ pageName }}</span>
|
||||||
|
<el-button type="primary" class="w-[100px]" @click="dialogVisible = true">{{ t('addDiyForm') }}</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||||
|
<el-form :inline="true" :model="diyFormTableData.searchParam" ref="searchFormDiyFormRef">
|
||||||
|
<el-form-item :label="t('title')" prop="title">
|
||||||
|
<el-input v-model.trim="diyFormTableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
|
||||||
|
</el-form-item>
|
||||||
|
<!-- <el-form-item :label="t('forAddon')" prop="addon_name">-->
|
||||||
|
<!-- <el-select v-model="diyFormTableData.searchParam.addon_name" :placeholder="t('forAddonPlaceholder')" @change="handleSelectAddonChange">-->
|
||||||
|
<!-- <el-option :label="t('all')" value="" />-->
|
||||||
|
<!-- <el-option v-for="(item, key) in apps" :label="item.title" :value="key" :key="key"/>-->
|
||||||
|
<!-- </el-select>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<el-form-item :label="t('typeName')" prop="type">
|
||||||
|
<el-select v-model="diyFormTableData.searchParam.type" :placeholder="t('formTypePlaceholder')">
|
||||||
|
<el-option :label="t('all')" value="" />
|
||||||
|
<el-option v-for="(item, key) in formType" :label="item.title" :value="key" :key="key"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="loadDiyFormList()">{{ t('search') }}</el-button>
|
||||||
|
<el-button @click="resetForm(searchFormDiyFormRef)">{{ t('reset') }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
<div class="mb-[10px] flex items-center">
|
||||||
|
<el-checkbox v-model="toggleCheckbox" size="large" class="px-[14px]" @change="toggleChange" :indeterminate="isIndeterminate" />
|
||||||
|
<el-button @click="batchDeleteForms" size="small">{{t("batchDeletion")}}</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="diyFormTableData.data" size="large" ref="diyFormListTableRef" v-loading="diyFormTableData.loading" @selection-change="handleSelectionChange">
|
||||||
|
<template #empty>
|
||||||
|
<span>{{ !diyFormTableData.loading ? t('emptyData') : '' }}</span>
|
||||||
|
</template>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="page_title" :label="t('title')" min-width="120" />
|
||||||
|
<!-- <el-table-column prop="addon_name" :label="t('forAddon')" min-width="80" />-->
|
||||||
|
<el-table-column prop="type_name" :label="t('typeName')" min-width="80" />
|
||||||
|
<el-table-column :label="t('status')" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag type="success" v-if="row.status == 1" class="cursor-pointer" @click="showClick(row)">{{ t('statusOn') }}</el-tag>
|
||||||
|
<el-tag type="info" v-else class="cursor-pointer" @click="showClick(row)">{{ t('statusOff') }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="update_time" :label="t('updateTime')" min-width="120" />
|
||||||
|
|
||||||
|
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center justify-end">
|
||||||
|
<el-button type="primary" v-if="row.status == 1" link @click="spreadEvent(row)">{{ t('推广') }}</el-button>
|
||||||
|
<!-- <el-button type="primary" link @click="toPreview(row)">{{ t('preview') }}</el-button>-->
|
||||||
|
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||||
|
<el-button v-if="row.status == 0" type="primary" link @click="deleteEvent(row.form_id)">{{ t('delete') }}</el-button>
|
||||||
|
<el-button type="primary" link @click="detailEvent(row)">{{ t('详情') }}</el-button>
|
||||||
|
<el-dropdown placement="bottom" trigger="click" class="ml-[12px]">
|
||||||
|
<el-button type="primary" link>更多</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button type="primary" class="w-full" link @click="submitConfigEvent(row)">{{ t('提交成功页') }}</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button type="primary" class="w-full" link @click="writeConfigEvent(row)">{{ t('填写设置') }}</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button type="primary" class="w-full" link @click="openShare(row)">{{ t('shareSet') }}</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button type="primary" class="w-full" link @click="exportEvent(row)">{{ t('导出') }}</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button type="primary" class="w-full" link @click="copyEvent(row.form_id)">{{ t('复制') }}</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
</el-table>
|
||||||
|
<div class="mt-[16px] flex justify-end">
|
||||||
|
<el-pagination v-model:current-page="diyFormTableData.page" v-model:page-size="diyFormTableData.limit"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper" :total="diyFormTableData.total"
|
||||||
|
@size-change="loadDiyFormList()" @current-change="loadDiyFormList" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!--添加表单-->
|
||||||
|
<el-dialog v-model="dialogVisible" :title="t('addFormTips')" width="980px">
|
||||||
|
<el-form :model="formData" ref="formRef" :rules="formRules">
|
||||||
|
<!-- <el-form-item :label="t('title')" prop="title">-->
|
||||||
|
<!-- <el-input v-model.trim="formData.title" :placeholder="t('titlePlaceholder')" clearable maxlength="12" show-word-limit class="w-full" />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<el-form-item prop="type">
|
||||||
|
<div class="image-selection-container">
|
||||||
|
<div
|
||||||
|
v-for="(item, key) in formType"
|
||||||
|
:key="key"
|
||||||
|
class="image-option"
|
||||||
|
:class="{ selected: formData.type === key }"
|
||||||
|
@click="selectType(key)"
|
||||||
|
>
|
||||||
|
<img :src="img(item.preview)" class="option-image" />
|
||||||
|
<div class="option-title">{{ item.title }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <el-select v-model="formData.type" :placeholder="t('formTypePlaceholder')" class="!w-full">
|
||||||
|
<el-option v-for="(item, key) in formType" :label="item.title" :value="key" :key="key"/>
|
||||||
|
</el-select> -->
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="addEvent(formRef)">{{ t('confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 分享设置-->
|
||||||
|
<el-dialog v-model="shareDialogVisible" :title="t('shareSet')" width="30%">
|
||||||
|
<el-tabs v-model="tabShareType">
|
||||||
|
<el-tab-pane :label="t('wechat')" name="wechat"></el-tab-pane>
|
||||||
|
<el-tab-pane :label="t('weapp')" name="weapp"></el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<el-form :model="shareFormData[tabShareType]" label-width="90px" ref="shareFormRef" :rules="shareFormRules">
|
||||||
|
<el-form-item :label="t('sharePage')">
|
||||||
|
<span>{{ sharePage }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('shareTitle')" prop="title">
|
||||||
|
<el-input v-model.trim="shareFormData[tabShareType].title" :placeholder="t('shareTitlePlaceholder')" clearable maxlength="30" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('shareDesc')" prop="desc" v-if="tabShareType == 'wechat'">
|
||||||
|
<el-input v-model.trim="shareFormData[tabShareType].desc" :placeholder="t('shareDescPlaceholder')" type="textarea" rows="4" clearable maxlength="100" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('shareImageUrl')" prop="url">
|
||||||
|
<upload-image v-model="shareFormData[tabShareType].url" :limit="1" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="shareDialogVisible = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="shareEvent(shareFormRef)">{{ t('confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 推广弹出框 -->
|
||||||
|
<form-spread-popup ref="formSpreadPopupRef" />
|
||||||
|
|
||||||
|
<!-- 表单提交成功页弹出框 -->
|
||||||
|
<form-submit-popup ref="formSubmitPopupRef" @complete="loadDiyFormList" />
|
||||||
|
|
||||||
|
<!-- 表单填写设置弹出框 -->
|
||||||
|
<form-write-popup ref="formWritePopupRef" @complete="loadDiyFormList" />
|
||||||
|
|
||||||
|
<records-detail ref="recordsDetailDialog"/>
|
||||||
|
|
||||||
|
<!-- 表单明细导出弹出框 -->
|
||||||
|
<export-sure ref="exportSureDialog" :show="flag" type="diy_form_records" :searchParam="diyFormDetailData" @close="handleExportClose" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, computed } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { getFormType, getApps, getDiyFormPageList, deleteDiyForm, editDiyFormShare, editFormStatus, copyForm } from '@/app/api/diy_form'
|
||||||
|
import { FormInstance, ElMessage, ElMessageBox } from "element-plus";
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
|
||||||
|
import { img } from '@/utils/common'
|
||||||
|
import recordsDetail from '@/app/views/diy_form/records.vue'
|
||||||
|
import formSpreadPopup from '@/app/views/diy_form/components/form-spread-popup.vue'
|
||||||
|
import formSubmitPopup from '@/app/views/diy_form/components/form-submit-popup.vue'
|
||||||
|
import formWritePopup from '@/app/views/diy_form/components/form-write-popup.vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const pageName = route.meta.title
|
||||||
|
const repeat = ref(false)
|
||||||
|
|
||||||
|
const formType: any = reactive({}) // 表单类型
|
||||||
|
|
||||||
|
// 添加自定义表单
|
||||||
|
const formData = reactive({
|
||||||
|
title: '',
|
||||||
|
type: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
//详情
|
||||||
|
const recordsDetailDialog: Record<string, any> | null = ref(null)
|
||||||
|
const detailEvent=(row: any)=>{
|
||||||
|
let data = {form_id: row.form_id};
|
||||||
|
recordsDetailDialog.value.setFormData(data);
|
||||||
|
recordsDetailDialog.value.showDialog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const formRules = computed(() => {
|
||||||
|
return {
|
||||||
|
title: [
|
||||||
|
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' }
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
{ required: true, message: t('formTypePlaceholder'), trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const addEvent = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
|
||||||
|
await formEl.validate(async(valid) => {
|
||||||
|
if (valid) {
|
||||||
|
const query = { type: formData.type } // , title: formData.title
|
||||||
|
const url = router.resolve({
|
||||||
|
path: '/decorate/form/edit',
|
||||||
|
query
|
||||||
|
})
|
||||||
|
window.open(url.href)
|
||||||
|
dialogVisible.value = false
|
||||||
|
formData.title = ''
|
||||||
|
formData.type = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const showClick = (row: any) => {
|
||||||
|
row.status = row.status === 1 ? 0 : 1
|
||||||
|
const obj = {
|
||||||
|
form_id: row.form_id,
|
||||||
|
status: row.status,
|
||||||
|
}
|
||||||
|
editFormStatus(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取万能表单类型
|
||||||
|
const loadFormType = (addon = '')=> {
|
||||||
|
getFormType({}).then(res => {
|
||||||
|
for (let key in formType) {
|
||||||
|
delete formType[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in res.data) {
|
||||||
|
formType[key] = res.data[key]
|
||||||
|
}
|
||||||
|
formData.type = Object.keys(formType)[0]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFormType();
|
||||||
|
|
||||||
|
const apps: any = reactive({}) // 应用插件列表
|
||||||
|
|
||||||
|
// todo 靠后完善
|
||||||
|
// getApps({}).then(res=>{
|
||||||
|
// if(res.data){
|
||||||
|
// for (const key in res.data) {
|
||||||
|
// apps[key] = res.data[key];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// 根据所属插件,查询表单类型
|
||||||
|
const handleSelectAddonChange = (value: any) => {
|
||||||
|
diyFormTableData.searchParam.type = '';
|
||||||
|
loadFormType(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const diyFormTableData: any = reactive({
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: true,
|
||||||
|
data: [],
|
||||||
|
searchParam: {
|
||||||
|
title: '',
|
||||||
|
type: '',
|
||||||
|
mode: '',
|
||||||
|
addon_name: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchFormDiyFormRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 获取自定义表单列表
|
||||||
|
const loadDiyFormList = (page: number = 1) => {
|
||||||
|
diyFormTableData.loading = true
|
||||||
|
diyFormTableData.page = page
|
||||||
|
|
||||||
|
getDiyFormPageList({
|
||||||
|
page: diyFormTableData.page,
|
||||||
|
limit: diyFormTableData.limit,
|
||||||
|
...diyFormTableData.searchParam
|
||||||
|
}).then(res => {
|
||||||
|
diyFormTableData.loading = false
|
||||||
|
diyFormTableData.data = res.data.data
|
||||||
|
diyFormTableData.total = res.data.total
|
||||||
|
setTablePageStorage(diyFormTableData.page, diyFormTableData.limit, diyFormTableData.searchParam);
|
||||||
|
}).catch(() => {
|
||||||
|
diyFormTableData.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDiyFormList(getTablePageStorage(diyFormTableData.searchParam).page);
|
||||||
|
|
||||||
|
const selectType = (index: number) => {
|
||||||
|
formData.type = index.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
formEl.resetFields()
|
||||||
|
loadDiyFormList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑自定义表单
|
||||||
|
const editEvent = (data: any) => {
|
||||||
|
const url = router.resolve({
|
||||||
|
path: '/decorate/form/edit',
|
||||||
|
query: { form_id: data.form_id }
|
||||||
|
})
|
||||||
|
window.open(url.href)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制页面
|
||||||
|
const copyEvent = (id: any) => {
|
||||||
|
ElMessageBox.confirm(t('diyFormCopyTips'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
if (repeat.value) return
|
||||||
|
repeat.value = true
|
||||||
|
|
||||||
|
copyForm({form_id: id}).then((res: any) => {
|
||||||
|
if (res.code == 1) {
|
||||||
|
loadDiyFormList()
|
||||||
|
}
|
||||||
|
repeat.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
repeat.value = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除自定义表单
|
||||||
|
const deleteEvent = (form_id: number) => {
|
||||||
|
ElMessageBox.confirm(t('diyFormDeleteTips'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
deleteDiyForm({ form_ids: [form_id] }).then(() => {
|
||||||
|
loadDiyFormList()
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 批量复选框
|
||||||
|
const toggleCheckbox = ref();
|
||||||
|
|
||||||
|
// 复选框中间状态
|
||||||
|
const isIndeterminate = ref(false);
|
||||||
|
|
||||||
|
// 监听批量复选框事件
|
||||||
|
const toggleChange = (value: any) => {
|
||||||
|
isIndeterminate.value = false;
|
||||||
|
diyFormListTableRef.value.toggleAllSelection();
|
||||||
|
};
|
||||||
|
|
||||||
|
const diyFormListTableRef = ref();
|
||||||
|
|
||||||
|
// 选中数据
|
||||||
|
const multipleSelection: any = ref([]);
|
||||||
|
|
||||||
|
// 监听表格单行选中
|
||||||
|
const handleSelectionChange = (val: []) => {
|
||||||
|
multipleSelection.value = val;
|
||||||
|
|
||||||
|
toggleCheckbox.value = false;
|
||||||
|
if (
|
||||||
|
multipleSelection.value.length > 0 &&
|
||||||
|
multipleSelection.value.length < diyFormTableData.data.length
|
||||||
|
) {
|
||||||
|
isIndeterminate.value = true;
|
||||||
|
} else {
|
||||||
|
isIndeterminate.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multipleSelection.value.length == diyFormTableData.data.length) {
|
||||||
|
toggleCheckbox.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
const batchDeleteForms = () => {
|
||||||
|
if (multipleSelection.value.length == 0) {
|
||||||
|
ElMessage({
|
||||||
|
type: "warning",
|
||||||
|
message: `${t("batchEmptySelectedFormsTips")}`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessageBox.confirm(t("batchFormsDeleteTips"), t("warning"), {
|
||||||
|
confirmButtonText: t("confirm"),
|
||||||
|
cancelButtonText: t("cancel"),
|
||||||
|
type: "warning",
|
||||||
|
}).then(() => {
|
||||||
|
if (repeat.value) return;
|
||||||
|
repeat.value = true;
|
||||||
|
|
||||||
|
const form_ids: any = [];
|
||||||
|
multipleSelection.value.forEach((item: any) => {
|
||||||
|
form_ids.push(item.form_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
deleteDiyForm({
|
||||||
|
form_ids: form_ids,
|
||||||
|
}).then(() => {
|
||||||
|
loadDiyFormList();
|
||||||
|
repeat.value = false;
|
||||||
|
}).catch(() => {
|
||||||
|
repeat.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转去预览
|
||||||
|
const toPreview = (data: any) => {
|
||||||
|
const url = router.resolve({
|
||||||
|
path: '/preview/wap',
|
||||||
|
query: {
|
||||||
|
page: '/app/pages/index/diy_form?form_id=' + data.form_id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.open(url.href)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabShareType = ref('wechat')
|
||||||
|
const sharePage = ref('')
|
||||||
|
const shareFormId = ref(0)
|
||||||
|
const shareFormData = reactive({
|
||||||
|
wechat: {
|
||||||
|
title: '',
|
||||||
|
desc: '',
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
weapp: {
|
||||||
|
title: '',
|
||||||
|
url: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const shareDialogVisible = ref(false)
|
||||||
|
const shareFormRules = computed(() => {
|
||||||
|
return {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const shareFormRef = ref<FormInstance>()
|
||||||
|
const openShare = async (row: any) => {
|
||||||
|
shareFormId.value = row.form_id
|
||||||
|
sharePage.value = row.title
|
||||||
|
const share = row.share
|
||||||
|
shareFormData.wechat = share.wechat
|
||||||
|
shareFormData.weapp = share.weapp
|
||||||
|
|
||||||
|
shareDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareEvent = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
|
||||||
|
await formEl.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
editDiyFormShare({
|
||||||
|
form_id: shareFormId.value,
|
||||||
|
share: JSON.stringify(shareFormData)
|
||||||
|
}).then(() => {
|
||||||
|
loadDiyFormList()
|
||||||
|
shareDialogVisible.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单推广
|
||||||
|
const formSpreadPopupRef: any = ref(null)
|
||||||
|
|
||||||
|
const spreadEvent = (data: any) => {
|
||||||
|
formSpreadPopupRef.value.show(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单提交成功页弹出框
|
||||||
|
const formSubmitPopupRef: any = ref(null)
|
||||||
|
|
||||||
|
const submitConfigEvent = (data: any) => {
|
||||||
|
formSubmitPopupRef.value.setFormData(data)
|
||||||
|
formSubmitPopupRef.value.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单填写设置弹出框
|
||||||
|
const formWritePopupRef: any = ref(null)
|
||||||
|
|
||||||
|
const writeConfigEvent = (data: any) => {
|
||||||
|
formWritePopupRef.value.setFormData(data)
|
||||||
|
formWritePopupRef.value.showDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单填写记录明细导出
|
||||||
|
*/
|
||||||
|
const exportSureDialog = ref(null)
|
||||||
|
const flag = ref(false)
|
||||||
|
const handleExportClose = (val) => {
|
||||||
|
flag.value = val
|
||||||
|
}
|
||||||
|
|
||||||
|
const diyFormDetailData: any = reactive({
|
||||||
|
form_id: 0,
|
||||||
|
})
|
||||||
|
const exportEvent = (data: any) => {
|
||||||
|
diyFormDetailData.form_id = data.form_id
|
||||||
|
flag.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.image-selection-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.image-option {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 300px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.image-option.selected {
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
box-shadow: 0 0 10px var(--el-color-primary-light-9);
|
||||||
|
}
|
||||||
|
.option-image {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
.option-title {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
374
admin/src/app/views/diy_form/records.vue
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer v-model="showDialog" :title="t('数据与统计')" direction="rtl" size="70%" :before-close="handleClose" class="member-detail-drawer">
|
||||||
|
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||||
|
<el-tab-pane :label="t('明细数据')" name="detail_data">
|
||||||
|
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||||
|
<el-form :inline="true" :model="formData.searchParam" ref="searchFormDiyFormRef">
|
||||||
|
<el-form-item :label="t('填表人')" prop="keyword">
|
||||||
|
<el-input v-model.trim="formData.searchParam.keyword" placeholder="请输入填表人" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('填表时间')" prop="create_time">
|
||||||
|
<el-date-picker v-model="formData.searchParam.create_time" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="loadFormRecordsListFn()">{{ t('search') }}</el-button>
|
||||||
|
<el-button @click="resetForm(searchFormDiyFormRef)">{{ t('reset') }}</el-button>
|
||||||
|
<el-button type="primary" @click="exportEvent">{{ t('export') }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-table :data="formData.data" size="large" v-loading="formData.loading">
|
||||||
|
<template #empty>
|
||||||
|
<span>{{ !formData.loading ? t('emptyData') : '' }}</span>
|
||||||
|
</template>
|
||||||
|
<el-table-column fixed :label="t('填表人信息')" min-width="160">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center cursor-pointer" @click="detailEvent(row.member.member_id)">
|
||||||
|
<div class="min-w-[50px] h-[50px] flex items-center justify-center">
|
||||||
|
<el-image v-if="row.member.headimg" class="w-[50px] h-[50px]" :src="img(row.member.headimg)" fit="contain">
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<img class="w-[50px] h-[50px] rounded-full" src="@/app/assets/images/member_head.png" alt="">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<img class="w-[50px] h-[50px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="ml-2">
|
||||||
|
<span :title="(row.member.nickname || row.member.username)" class="multi-hidden">{{row.member.nickname || row.member.username}}</span>
|
||||||
|
<span class="text-primary text-[12px]">{{row.member.mobile || ''}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column fixed prop="create_time" :label="t('填表时间')" min-width="120" />
|
||||||
|
|
||||||
|
<el-table-column v-for="item in formFieldsList" :key="item.field_key" :label="item.field_name" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<!-- 动态渲染表单组件内容 -->
|
||||||
|
<template v-if="row.recordsFieldList[item.field_key]">
|
||||||
|
<component :is="row.recordsFieldList[item.field_key].detailComponent" :data="row.recordsFieldList[item.field_key]"/>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="70">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<!-- <el-button type="primary" link @click="formDetailEvent(row)">{{ t('详情') }}</el-button> -->
|
||||||
|
<el-button type="primary" link @click="deleteEvent(row)">{{ t('delete') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
</el-table>
|
||||||
|
<div class="mt-[16px] flex justify-end">
|
||||||
|
<el-pagination v-model:current-page="formData.page" v-model:page-size="formData.limit" :page-sizes="[6,10,20,30,50,100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper" :total="formData.total"
|
||||||
|
@size-change="loadFormRecordsListFn()" @current-change="loadFormRecordsListFn" />
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="t('填表人统计')" name="member_stat">
|
||||||
|
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||||
|
<el-form :inline="true" :model="formMemberList.searchParam" ref="searchFormDiyMemberRef">
|
||||||
|
<el-form-item :label="t('填表人')" prop="keyword">
|
||||||
|
<el-input v-model.trim="formMemberList.searchParam.keyword" placeholder="请输入填表人" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="getFormRecordsMemberFn()">{{ t('search') }}</el-button>
|
||||||
|
<el-button @click="resetFormMember(searchFormDiyMemberRef)">{{ t('reset') }}</el-button>
|
||||||
|
<el-button type="primary" @click="exportMemberEvent">{{ t('export') }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
<el-table :data="formMemberList.data" size="large" v-loading="formMemberList.loading">
|
||||||
|
<template #empty>
|
||||||
|
<span>{{ !formMemberList.loading ? t('emptyData') : '' }}</span>
|
||||||
|
</template>
|
||||||
|
<el-table-column fixed :label="t('填表人信息')" min-width="200">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center cursor-pointer" @click="detailEvent(row.member.member_id)">
|
||||||
|
<div class="min-w-[50px] h-[50px] flex items-center justify-center">
|
||||||
|
<el-image v-if="row.member.headimg" class="w-[50px] h-[50px]" :src="img(row.member.headimg)" fit="contain">
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<img class="w-[50px] h-[50px] rounded-full" src="@/app/assets/images/member_head.png" alt="">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<img class="w-[50px] h-[50px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="ml-2">
|
||||||
|
<span :title="(row.member.nickname || row.member.username)" class="multi-hidden">{{row.member.nickname || row.member.username}}</span>
|
||||||
|
<span class="text-primary text-[12px]">{{row.member.mobile || ''}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- <el-table-column fixed prop="create_time" :label="t('填表时间')" min-width="120" /> -->
|
||||||
|
<el-table-column fixed prop="create_time" :label="t('总计(表单填写数)')" min-width="500">
|
||||||
|
<template #default="{ row }" @click="">
|
||||||
|
{{ row.write_count }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
</el-table>
|
||||||
|
<div class="mt-[16px] flex justify-end">
|
||||||
|
<el-pagination v-model:current-page="formMemberList.page" v-model:page-size="formMemberList.limit"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper" :total="formMemberList.total"
|
||||||
|
@size-change="getFormRecordsMemberFn()" @current-change="getFormRecordsMemberFn()" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="t('字段统计')" name="field_stat">
|
||||||
|
<el-collapse v-model="activeNames" class="diy-collapse mt-[15px]">
|
||||||
|
<el-collapse-item :title="item.field_name" :name="item.field_id" v-for="(item, index) in formFieldsStat" :key="index">
|
||||||
|
<template #title>
|
||||||
|
<div class="text-[16px] font-bold">{{item.field_name}}</div>
|
||||||
|
</template>
|
||||||
|
<el-table :data="item.value_list" border>
|
||||||
|
<el-table-column :label="item.field_name" prop="render_value">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{row.render_value ? row.render_value : '未填写'}}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="小计" prop="write_count"></el-table-column>
|
||||||
|
<el-table-column label="比例">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-progress :percentage="row.write_percent"></el-progress>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" :title="t('查看信息')" width="400px">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex mb-[10px]" v-for="(item, index) in formDetail" :key="index">
|
||||||
|
<div class="flex justify-end w-[100px]">{{ item.label }}:</div>
|
||||||
|
<div class="flex ml-[20px]">
|
||||||
|
<div v-if="Array.isArray(item.text)" class="mr-[10px]" v-for="(textItem, i) in item.text" :key="i">
|
||||||
|
{{ textItem }}
|
||||||
|
</div>
|
||||||
|
<div v-else>{{ item.text }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="dialogVisible = false">{{ t('confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
</el-drawer>
|
||||||
|
<export-sure ref="exportSureDialog" :show="flag" type="diy_form_records" :searchParam="formData.searchParam" @close="handleExportClose" />
|
||||||
|
<export-sure ref="exportSureDialog" :show="flagMember" type="diy_form_records_member" :searchParam="formMemberList.searchParam" @close="handleMemberExportClose" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, defineAsyncComponent } from 'vue'
|
||||||
|
import { t } from '@/lang'
|
||||||
|
import { getDiyFormFieldsList, getDiyFormFieldStat, getFormRecords,getFormRecordsInfo,deleteFormRecords,getFormRecordsMember} from '@/app/api/diy_form'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { img } from '@/utils/common'
|
||||||
|
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const showDialog = ref(false)
|
||||||
|
const activeName = ref('detail_data')
|
||||||
|
const formId = ref(0)
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const searchFormDiyFormRef = ref<FormInstance>()
|
||||||
|
const searchFormDiyMemberRef = ref<FormInstance>()
|
||||||
|
const handleClose = (done: () => void) => {
|
||||||
|
showDialog.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
page: 1,
|
||||||
|
limit: 6,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
data: [],
|
||||||
|
searchParam: {
|
||||||
|
form_id: 0,
|
||||||
|
keyword: '',
|
||||||
|
create_time: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formFieldsList = ref([])
|
||||||
|
|
||||||
|
// 获取万能表单字段列表
|
||||||
|
const getDiyFormFieldsListFn = (form_id:any)=>{
|
||||||
|
getDiyFormFieldsList({
|
||||||
|
form_id,
|
||||||
|
order: 'field_id',
|
||||||
|
sort: 'asc'
|
||||||
|
}).then((res:any)=>{
|
||||||
|
formFieldsList.value = res.data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取字段统计列表
|
||||||
|
const formFieldsStat = ref([])
|
||||||
|
const getDiyFormFieldStatFn = (form_id:any)=>{
|
||||||
|
getDiyFormFieldStat({
|
||||||
|
form_id
|
||||||
|
}).then((res:any)=>{
|
||||||
|
formFieldsStat.value = res.data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const modules: any = import.meta.glob('@/**/*.vue')
|
||||||
|
const formDetail = ref([])
|
||||||
|
|
||||||
|
const formDetailEvent = (row: any) => {
|
||||||
|
getFormRecordsInfo(row.record_id).then((res:any)=>{
|
||||||
|
formDetail.value = res.data.value
|
||||||
|
dialogVisible.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const deleteEvent = (row: any) => {
|
||||||
|
ElMessageBox.confirm(t('确定删除该条数据吗'), t('warning'),
|
||||||
|
{
|
||||||
|
confirmButtonText: t('confirm'),
|
||||||
|
cancelButtonText: t('cancel'),
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
deleteFormRecords({
|
||||||
|
record_id: row.record_id,
|
||||||
|
form_id: row.form_id
|
||||||
|
}).then(() => {
|
||||||
|
initData();
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
formEl.resetFields()
|
||||||
|
loadFormRecordsListFn()
|
||||||
|
}
|
||||||
|
const resetFormMember = (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
formEl.resetFields()
|
||||||
|
getFormRecordsMemberFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadFormRecordsListFn= (page: number = 1)=>{
|
||||||
|
formData.loading = true
|
||||||
|
formData.page = page
|
||||||
|
getFormRecords({
|
||||||
|
page: formData.page,
|
||||||
|
limit: formData.limit,
|
||||||
|
...formData.searchParam
|
||||||
|
}).then((res:any)=>{
|
||||||
|
formData.loading = false
|
||||||
|
formData.data = res.data.data
|
||||||
|
formData.data.forEach((item:any)=>{
|
||||||
|
for (let key:any in item.recordsFieldList){
|
||||||
|
if (modules[item.recordsFieldList[key].detailComponent]) {
|
||||||
|
item.recordsFieldList[key].detailComponent && (item.recordsFieldList[key].detailComponent = defineAsyncComponent(modules[item.recordsFieldList[key].detailComponent]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
formData.total = res.data.total
|
||||||
|
}).catch(() => {
|
||||||
|
formData.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const formMemberList = reactive({
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
total: 0,
|
||||||
|
loading: false,
|
||||||
|
data: [],
|
||||||
|
searchParam: {
|
||||||
|
keyword: '',
|
||||||
|
form_id: 0,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getFormRecordsMemberFn = (page: number = 1) => {
|
||||||
|
formMemberList.loading = true
|
||||||
|
formMemberList.page = page
|
||||||
|
getFormRecordsMember({
|
||||||
|
page: formMemberList.page,
|
||||||
|
limit: formMemberList.limit,
|
||||||
|
...formMemberList.searchParam
|
||||||
|
}).then((res: any) => {
|
||||||
|
formMemberList.data = res.data.data;
|
||||||
|
formMemberList.total = res.total;
|
||||||
|
formMemberList.loading = false;
|
||||||
|
}).catch((error) => {
|
||||||
|
formMemberList.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//查看会员详情
|
||||||
|
const detailEvent = (member_id:number)=> {
|
||||||
|
let routeData = router.resolve(`/member/detail?id=${member_id}`)
|
||||||
|
window.open(routeData.href, ' blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFormData = async (row: any = null) => {
|
||||||
|
formId.value = row.form_id;
|
||||||
|
formData.searchParam.form_id = row.form_id;
|
||||||
|
formMemberList.searchParam.form_id = row.form_id;
|
||||||
|
|
||||||
|
getDiyFormFieldsListFn(row.form_id);
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
|
||||||
|
const initData = () =>{
|
||||||
|
getFormRecordsMemberFn();
|
||||||
|
getDiyFormFieldStatFn(formId.value);
|
||||||
|
loadFormRecordsListFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单填写记录明细导出
|
||||||
|
*/
|
||||||
|
const exportSureDialog = ref(null)
|
||||||
|
const flag = ref(false)
|
||||||
|
const handleExportClose = (val) => {
|
||||||
|
flag.value = val
|
||||||
|
}
|
||||||
|
const exportEvent = () => {
|
||||||
|
flag.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const flagMember = ref(false)
|
||||||
|
const handleMemberExportClose = (val) => {
|
||||||
|
flagMember.value = val
|
||||||
|
}
|
||||||
|
const exportMemberEvent = () => {
|
||||||
|
flagMember.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
showDialog,
|
||||||
|
setFormData
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.diy-collapse .el-collapse-item__header{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
justify-content: flex-end;
|
||||||
|
.el-icon.el-collapse-item__arrow{
|
||||||
|
margin-left: inherit;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -3,7 +3,7 @@
|
|||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<slot name="content">
|
<slot name="content">
|
||||||
<div>
|
<div>
|
||||||
<img class="w-[300px]" src="@/app/assets/images/error.png" />
|
<img class="w-[240px]" src="@/app/assets/images/error.png" />
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
<div class="text-left ml-[100px]">
|
<div class="text-left ml-[100px]">
|
||||||
|
|||||||
@ -44,8 +44,8 @@
|
|||||||
:placeholder="t('cashOutNumberPlaceholder')" />
|
:placeholder="t('cashOutNumberPlaceholder')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('memberInfo')" prop="keyword">
|
<el-form-item :label="t('memberInfo')" prop="keywords">
|
||||||
<el-input v-model.trim="orderTableData.searchParam.keyword" class="w-[240px]"
|
<el-input v-model.trim="orderTableData.searchParam.keywords" class="w-[240px]"
|
||||||
:placeholder="t('memberInfoPlaceholder')" />
|
:placeholder="t('memberInfoPlaceholder')" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
@ -80,64 +80,109 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<div class="mt-[10px]">
|
<div class="mt-[10px]">
|
||||||
<el-table :data="orderTableData.data" size="large" v-loading="orderTableData.loading">
|
<el-table :data="orderTableData.data" size="large" class="table-top">
|
||||||
<template #empty>
|
<el-table-column :label="t('memberInfo')" min-width="180" />
|
||||||
<span>{{ !orderTableData.loading ? t('emptyData') : '' }}</span>
|
<el-table-column :label="t('cashOutMethod')" align="center" min-width="100" />
|
||||||
</template>
|
<el-table-column :label="t('cashOutInfo')" min-width="180" />
|
||||||
|
<el-table-column :label="t('applicationForWithdrawalAmount')" align="center" min-width="120" />
|
||||||
|
<el-table-column :label="t('actualTransferAmount')" align="center" min-width="120" />
|
||||||
|
<el-table-column :label="t('cashOutCommission')" align="center" min-width="110" />
|
||||||
|
<el-table-column :label="t('cashOutStatus')" align="center" min-width="100" />
|
||||||
|
<el-table-column :label="t('applyTime')" align="center" min-width="160" />
|
||||||
|
<el-table-column :label="t('auditTime')" align="center" min-width="160" />
|
||||||
|
<el-table-column :label="t('transferTime')" align="center" min-width="160" />
|
||||||
|
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120" />
|
||||||
|
</el-table>
|
||||||
|
<div class="table-body min-h-[150px]" v-loading="orderTableData.loading">
|
||||||
|
<div v-if="!orderTableData.loading">
|
||||||
|
<template v-if="orderTableData.data.length">
|
||||||
|
<div v-for="(item, index) in orderTableData.data" :key="index">
|
||||||
|
<el-table :data="[item]" size="large" :show-header="false">
|
||||||
|
<el-table-column :show-overflow-tooltip="true" min-width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center cursor-pointer " @click="toMember(row.member.member_id)">
|
||||||
|
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" alt="">
|
||||||
|
<img class="w-[50px] h-[50px] mr-[10px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
|
||||||
|
<div class="flex flex flex-col items-baseline" style="width: calc(100% - 60px);">
|
||||||
|
<span class="w-[100%] truncate text-left">{{ row.member.nickname || row.member.username || '' }}</span>
|
||||||
|
<span class="w-[100%] truncate">{{ row.member.mobile || '' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.transfer_type_name }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column min-width="180">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex flex-col" v-if="row.transfer_type=='wechat_code' || row.transfer_type=='alipay'">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="w-[70px] flex-shrink-0 text-right">{{t('realname') }}:</span>
|
||||||
|
<span class="using-hidden">{{ row.transfer_realname }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="w-[70px] flex-shrink-0 text-right">{{t('account') }}:</span>
|
||||||
|
<span>{{ row.transfer_account }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center" v-if="row.transfer_payment_code">
|
||||||
|
<span class="w-[70px] flex-shrink-0 text-right">{{ t('transferCode') }}:</span>
|
||||||
|
<el-image :src="img(row.transfer_payment_code)" :preview-src-list="[img(row.transfer_payment_code)]" :hide-on-click-modal="true" class="w-[50px] h-[50px]"></el-image>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col" v-else-if="row.transfer_type=='bank'">
|
||||||
|
<span>{{t('bankRealname') }}:{{ row.transfer_realname }}</span>
|
||||||
|
<span>{{ t('bankAccount') }}:{{ row.transfer_account }}</span>
|
||||||
|
<span>{{ t('bankName') }}:{{ row.transfer_bank }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="apply_money" min-width="120" align="center" />
|
||||||
|
|
||||||
<el-table-column prop="order_no" :show-overflow-tooltip="true" :label="t('memberInfo')" align="center" min-width="140">
|
<el-table-column prop="money" min-width="120" align="center" />
|
||||||
<template #default="{ row }">
|
|
||||||
<div class="flex items-center cursor-pointer " @click="toMember(row.member.member_id)">
|
<el-table-column prop="service_money" align="center" min-width="110" />
|
||||||
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" alt="">
|
|
||||||
<img class="w-[50px] h-[50px] mr-[10px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
|
<el-table-column prop="status_name" align="center" min-width="100" />
|
||||||
<div class="flex flex flex-col">
|
|
||||||
<span>{{ row.member.nickname || '' }}</span>
|
<el-table-column min-width="160" align="center">
|
||||||
<span>{{ row.member.mobile || '' }}</span>
|
<template #default="{ row }">
|
||||||
</div>
|
{{ row.create_time || '' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column min-width="160" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.audit_time || '' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column min-width="160" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.transfer_time || '' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column align="right" fixed="right" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button v-for="(item, index) in operationBtn[row.status.toString()].value" :key="index + 'a'"
|
||||||
|
@click="fnProcessing(operationBtn[row.status.toString()].clickArr[index], row)"
|
||||||
|
type="primary" link>{{ item }}</el-button>
|
||||||
|
<el-button type="primary" link @click="handleRemark(row)"> {{ t('remark') }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div v-if="item.remark" class="text-[14px] min-h-[30px] leading-[30px] px-3 bg-[#fff0e5] text-[#ff7f5b] mb-[10px] relative remark">
|
||||||
|
<span class="mr-[5px]">{{ t('notes') }}:</span>
|
||||||
|
<span>{{ item.remark }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
<el-empty v-else :image-size="1" :description="t('emptyData')" />
|
||||||
<el-table-column :label="t('cashOutMethod')" align="center" min-width="140">
|
</div>
|
||||||
<template #default="{ row }">
|
</div>
|
||||||
{{ row.transfer_type_name }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column prop="apply_money" :label="t('applicationForWithdrawalAmount')" min-width="140" align="center" />
|
|
||||||
|
|
||||||
<el-table-column prop="money" :label="t('actualTransferAmount')" min-width="200" align="center" />
|
|
||||||
|
|
||||||
<el-table-column prop="service_money" :label="t('cashOutCommission')" align="center" min-width="140" />
|
|
||||||
|
|
||||||
<el-table-column prop="status_name" :label="t('cashOutStatus')" align="center" min-width="100" />
|
|
||||||
|
|
||||||
<el-table-column :label="t('applyTime')" min-width="180" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.create_time || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column :label="t('auditTime')" min-width="180" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.audit_time || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column :label="t('transferTime')" min-width="180" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
{{ row.transfer_time || '' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="120">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-button v-for="(item, index) in operationBtn[row.status.toString()].value" :key="index + 'a'"
|
|
||||||
@click="fnProcessing(operationBtn[row.status.toString()].clickArr[index], row)"
|
|
||||||
type="primary" link>{{ item }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
</el-table>
|
|
||||||
<div class="mt-[16px] flex justify-end">
|
<div class="mt-[16px] flex justify-end">
|
||||||
<el-pagination v-model:current-page="orderTableData.page" v-model:page-size="orderTableData.limit"
|
<el-pagination v-model:current-page="orderTableData.page" v-model:page-size="orderTableData.limit"
|
||||||
layout="total, sizes, prev, pager, next, jumper" :total="orderTableData.total"
|
layout="total, sizes, prev, pager, next, jumper" :total="orderTableData.total"
|
||||||
@ -147,42 +192,99 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- 详情 -->
|
<!-- 详情 -->
|
||||||
<el-dialog v-model="cashOutShowDialog" :title="t('cashOutDetail')" width="500px" :destroy-on-close="true">
|
<el-dialog v-model="cashOutShowDialog" :title="t('cashOutDetail')" width="650px" :destroy-on-close="true">
|
||||||
<el-form :model="cashOutInfo" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="cashOutLoading">
|
<el-form :model="cashOutInfo" label-width="120px" ref="formRef" class="page-form" v-loading="cashOutLoading">
|
||||||
<el-form-item :label="t('nickname')">
|
<el-row>
|
||||||
<div class="input-width"> {{ cashOutInfo.nickname }} </div>
|
<el-col :span="12">
|
||||||
</el-form-item>
|
<el-form-item :label="t('nickname')">
|
||||||
<el-form-item :label="t('cashOutAccountType')">
|
<div class="input-width"> {{ cashOutInfo.nickname|| cashOutInfo.username }} </div>
|
||||||
<div class="input-width"> {{ cashOutInfo.account_type_name }} </div>
|
</el-form-item>
|
||||||
</el-form-item>
|
</el-col>
|
||||||
<el-form-item :label="t('cashOutMethod')">
|
<el-col :span="12">
|
||||||
<div class="input-width"> {{ Transfertype[cashOutInfo.transfer_type].name }} </div>
|
<el-form-item :label="t('cashOutAccountType')">
|
||||||
</el-form-item>
|
<div class="input-width"> {{ cashOutInfo.account_type_name }} </div>
|
||||||
<template v-if="cashOutInfo.transfer_type == 'alipay'">
|
</el-form-item>
|
||||||
<el-form-item :label="t('alipayAccount')">
|
</el-col>
|
||||||
<div class="input-width"> {{ cashOutInfo.transfer_account }} </div>
|
<el-col :span="12">
|
||||||
</el-form-item>
|
<el-form-item :label="t('cashOutMethod')">
|
||||||
</template>
|
<div class="input-width"> {{ Transfertype[cashOutInfo.transfer_type].name }} </div>
|
||||||
<template v-if="cashOutInfo.transfer_type == 'bank'">
|
</el-form-item>
|
||||||
<el-form-item :label="t('bankName')">
|
</el-col>
|
||||||
<div class="input-width"> {{ cashOutInfo.transfer_bank }} </div>
|
<template v-if="cashOutInfo.transfer_type == 'alipay' || cashOutInfo.transfer_type == 'wechat_code'">
|
||||||
</el-form-item>
|
<el-col :span="12">
|
||||||
<el-form-item :label="t('bankAccount')">
|
<el-form-item :label="t('realname')">
|
||||||
<div class="input-width"> {{ cashOutInfo.transfer_account }} </div>
|
<div class="input-width"> {{ cashOutInfo.transfer_realname }} </div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</el-col>
|
||||||
<el-form-item :label="t('applicationForWithdrawalAmount')">
|
<el-col :span="12">
|
||||||
<div class="input-width"> {{ cashOutInfo.apply_money }} </div>
|
<el-form-item :label="t('alipayAccount')">
|
||||||
</el-form-item>
|
<div class="input-width"> {{ cashOutInfo.transfer_account }} </div>
|
||||||
<el-form-item :label="t('cashOutCommission')">
|
</el-form-item>
|
||||||
<div class="input-width"> {{ cashOutInfo.service_money }} </div>
|
</el-col>
|
||||||
</el-form-item>
|
<el-col :span="12">
|
||||||
<el-form-item :label="t('actualTransferAmount')">
|
<el-form-item :label="t('transferCode')">
|
||||||
<div class="input-width"> {{ cashOutInfo.money }} </div>
|
<el-image :src="img(cashOutInfo.transfer_payment_code)" :preview-src-list="[img(cashOutInfo.transfer_payment_code)]" :hide-on-click-modal="true" class="mr-[10px] w-[50px] h-[50px]"></el-image>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('cashOutStatus')">
|
</el-col>
|
||||||
<div class="input-width"> {{ cashOutInfo.status_name }} </div>
|
</template>
|
||||||
</el-form-item>
|
<template v-if="cashOutInfo.transfer_type == 'bank'">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankName')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.transfer_bank }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankAccount')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.transfer_account }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('applicationForWithdrawalAmount')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.apply_money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('cashOutCommission')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.service_money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('actualTransferAmount')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('cashOutStatus')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.status_name }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('applyTime')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.create_time }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('auditTime')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.audit_time }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" v-if="cashOutInfo.remark">
|
||||||
|
<el-form-item :label="t('remark')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.remark }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" v-if="cashOutInfo.transfer && cashOutInfo.transfer.transfer_voucher">
|
||||||
|
<el-form-item :label="t('transferVoucher')">
|
||||||
|
<el-image :src="img(cashOutInfo.transfer.transfer_voucher)" :preview-src-list="[img(cashOutInfo.transfer.transfer_voucher)]" :hide-on-click-modal="true" class="w-[50px] h-[50px]"></el-image>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" v-if="cashOutInfo.transfer && cashOutInfo.transfer.transfer_remark">
|
||||||
|
<el-form-item :label="t('transferRemark')">
|
||||||
|
<div class="input-width"> {{ cashOutInfo.transfer.transfer_remark }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -191,11 +293,92 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 审核通过 -->
|
||||||
<!-- 是否审核 -->
|
<el-dialog v-model="auditPassShowDialog" :title="t('passAudit')" width="650px" :destroy-on-close="true">
|
||||||
|
<el-form :model="curData" label-width="120px" ref="formRef" class="page-form">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('nickname')">
|
||||||
|
<div class="input-width"> {{ curData.member.nickname ||curData.member.username }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('cashOutAccountType')">
|
||||||
|
<div class="input-width"> {{ curData.account_type_name }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('cashOutMethod')">
|
||||||
|
<div class="input-width"> {{ curData.transfer_type_name }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<template v-if="curData.transfer_type == 'alipay' || curData.transfer_type == 'wechat_code'">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('realname')">
|
||||||
|
<div class="input-width"> {{ curData.transfer_realname }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('account')">
|
||||||
|
<div class="input-width"> {{ curData.transfer_account }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('transferCode')">
|
||||||
|
<el-image :src="img(curData.transfer_payment_code)" :preview-src-list="[img(curData.transfer_payment_code)]" :hide-on-click-modal="true" class="w-[50px] h-[50px]"></el-image>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
<template v-if="curData.transfer_type == 'bank'">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankName')">
|
||||||
|
<div class="input-width"> {{ curData.transfer_bank }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankRealname')">
|
||||||
|
<div class="input-width"> {{ curData.transfer_realname }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankAccount')">
|
||||||
|
<div class="input-width"> {{ curData.transfer_account }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('applicationForWithdrawalAmount')">
|
||||||
|
<div class="input-width"> {{ curData.apply_money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('cashOutCommission')">
|
||||||
|
<div class="input-width"> {{ curData.service_money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('actualTransferAmount')">
|
||||||
|
<div class="input-width"> {{ curData.money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('applyTime')">
|
||||||
|
<div class="input-width"> {{ curData.create_time }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="auditPassShowDialog = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="handlePass()">{{ t('confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<!-- 是否审核拒绝 -->
|
||||||
<el-dialog v-model="auditShowDialog" :title="t('rejectionAudit')" width="500px" :destroy-on-close="true">
|
<el-dialog v-model="auditShowDialog" :title="t('rejectionAudit')" width="500px" :destroy-on-close="true">
|
||||||
<el-form :model="auditFailure" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
<el-form :model="auditFailure" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||||
<el-form-item :label="t('reasonsRefusal')" prop="label_name">
|
<el-form-item :label="t('reasonsRefusal')" prop="refuse_reason">
|
||||||
<el-input v-model.trim="auditFailure.refuse_reason" clearable maxlength="200" :show-word-limit="true" :placeholder="t('reasonsRefusalPlaceholder')" :rows="4" class="input-width" type="textarea" />
|
<el-input v-model.trim="auditFailure.refuse_reason" clearable maxlength="200" :show-word-limit="true" :placeholder="t('reasonsRefusalPlaceholder')" :rows="4" class="input-width" type="textarea" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -208,12 +391,88 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 是否转账 -->
|
<!-- 是否转账 -->
|
||||||
<el-dialog v-model="transferShowDialog" :title="t('rejectionAudit')" width="500px" :destroy-on-close="true">
|
<el-dialog v-model="transferShowDialog" :title="t('transfer')" width="650px" :destroy-on-close="true">
|
||||||
<p>{{ t('isTransfer') }}</p>
|
<el-form :model="transferData" label-width="120px" ref="formRef" class="page-form">
|
||||||
|
<el-row>
|
||||||
|
<template v-if="transferData.transfer_type == 'alipay' || transferData.transfer_type == 'wechat_code'">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('realname')">
|
||||||
|
<div class="input-width"> {{ transferData.transfer_realname }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('account')">
|
||||||
|
<div class="input-width"> {{ transferData.transfer_account }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('transferCode')">
|
||||||
|
<el-image :src="img(transferData.transfer_payment_code)" :preview-src-list="[img(transferData.transfer_payment_code)]" :hide-on-click-modal="true" class="w-[50px] h-[50px]"></el-image>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
<template v-if="transferData.transfer_type == 'bank'">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankName')">
|
||||||
|
<div class="input-width"> {{ transferData.transfer_bank }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankRealname')">
|
||||||
|
<div class="input-width"> {{ transferData.transfer_realname }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('bankAccount')">
|
||||||
|
<div class="input-width"> {{ transferData.transfer_account }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('applicationForWithdrawalAmount')">
|
||||||
|
<div class="input-width"> {{ transferData.apply_money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('cashOutCommission')">
|
||||||
|
<div class="input-width"> {{ transferData.service_money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item :label="t('actualTransferAmount')">
|
||||||
|
<div class="input-width"> {{ transferData.money }} </div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<el-form :model="formTransfer" label-width="120px" ref="formTransferRef" :rules="formTransferRules" class="page-form">
|
||||||
|
<el-form-item :label="t('transferVoucher')" prop="transfer_voucher">
|
||||||
|
<upload-image v-model="formTransfer.transfer_voucher" :limit="1" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('transferRemark')" prop="transfer_remark">
|
||||||
|
<el-input v-model.trim="formTransfer.transfer_remark" type="textarea" rows="4" clearable
|
||||||
|
:placeholder="t('transferRemarkPlaceholder')" class="input-width" maxlength="200" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="transferShowDialog = false">{{ t('cancel') }}</el-button>
|
<el-button @click="transferShowDialog = false">{{ t('cancel') }}</el-button>
|
||||||
<el-button type="primary" @click="confirm()">{{ t('confirm') }}</el-button>
|
<el-button type="primary" @click="handleTransfer(formTransferRef)">{{ t('confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<!-- 备注 -->
|
||||||
|
<el-dialog v-model="remarkShowDialog" :title="t('remark')" width="500px" :destroy-on-close="true">
|
||||||
|
<el-form :model="formData" label-width="90px" ref="formRemarkRef" :rules="formRemarkRules" class="page-form">
|
||||||
|
<el-form-item :label="t('remark')" prop="remark">
|
||||||
|
<el-input v-model.trim="formData.remark" type="textarea" rows="4" clearable
|
||||||
|
:placeholder="t('remarkPlaceholder')" class="input-width" maxlength="200" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="remarkShowDialog = false">{{ t('cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="save(formRemarkRef)">{{ t('confirm') }}</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@ -221,9 +480,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref, computed } from 'vue'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
import { getCashOutList, getTransfertype, memberTransfer, memberAudit, getCashOutDetail, getCashOutStatusList, getCashOutStat } from '@/app/api/member'
|
import { getCashOutList, getTransfertype, memberTransfer, memberAudit, getCashOutDetail, getCashOutStatusList, getCashOutStat, memberRemark, memberCheck } from '@/app/api/member'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { ElMessageBox, FormInstance, FormRules } from 'element-plus'
|
import { ElMessageBox, FormInstance, FormRules } from 'element-plus'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
@ -234,7 +493,6 @@ const checkStatusList = async () => {
|
|||||||
cashOutStatusList.value = await (await getCashOutStatusList()).data
|
cashOutStatusList.value = await (await getCashOutStatusList()).data
|
||||||
}
|
}
|
||||||
checkStatusList()
|
checkStatusList()
|
||||||
const transferShowDialog = ref(false)
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const pageName = route.meta.title
|
const pageName = route.meta.title
|
||||||
@ -251,6 +509,10 @@ const operationBtn = ref<AnyObject>({
|
|||||||
value: [t('detail')],
|
value: [t('detail')],
|
||||||
clickArr: ['detailFn']
|
clickArr: ['detailFn']
|
||||||
},
|
},
|
||||||
|
4: {
|
||||||
|
value: [t('detail')],
|
||||||
|
clickArr: ['detailFn']
|
||||||
|
},
|
||||||
'-1': {
|
'-1': {
|
||||||
value: [t('detail')],
|
value: [t('detail')],
|
||||||
clickArr: ['detailFn']
|
clickArr: ['detailFn']
|
||||||
@ -276,7 +538,7 @@ const orderTableData = reactive({
|
|||||||
create_time: [],
|
create_time: [],
|
||||||
status: '',
|
status: '',
|
||||||
cash_out_no: '',
|
cash_out_no: '',
|
||||||
keyword: '',
|
keywords: '',
|
||||||
audit_time: '',
|
audit_time: '',
|
||||||
transfer_time: '',
|
transfer_time: '',
|
||||||
transfer_type: ''
|
transfer_type: ''
|
||||||
@ -339,17 +601,26 @@ const fnProcessing = (type: string, data: any) => {
|
|||||||
obj.id = data.id
|
obj.id = data.id
|
||||||
if (type == 'successfulAuditFn') {
|
if (type == 'successfulAuditFn') {
|
||||||
obj.action = 'agree'
|
obj.action = 'agree'
|
||||||
cashOutAuditFn(obj)
|
curData.value = data
|
||||||
|
auditPassShowDialog.value = true
|
||||||
} else {
|
} else {
|
||||||
obj.action = 'refuse'
|
obj.action = 'refuse'
|
||||||
auditFailure.value = Object.assign(auditFailure.value, obj)
|
auditFailure.value = Object.assign(auditFailure.value, obj)
|
||||||
auditShowDialog.value = true
|
auditShowDialog.value = true
|
||||||
}
|
}
|
||||||
} else if (type == 'transferFn') {
|
} else if (type == 'transferFn') {
|
||||||
obj.id = data.id
|
if (data.transfer_type == 'wechatpay') {
|
||||||
ElMessageBox.confirm(`${ t('isTransfer') }`, `${ t('transfer') }`).then(() => {
|
obj.id = data.id
|
||||||
transferFn(obj)
|
ElMessageBox.confirm(`${t('isTransfer')}`, `${t('transfer')}`).then(() => {
|
||||||
})
|
transferFn(obj)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
transferData.value = data
|
||||||
|
formTransfer.id = data.id
|
||||||
|
transferShowDialog.value = true
|
||||||
|
}
|
||||||
|
} else if (type == 'checkFn') {
|
||||||
|
checkFn(data.id)
|
||||||
} else {
|
} else {
|
||||||
detailFn(data.id)
|
detailFn(data.id)
|
||||||
}
|
}
|
||||||
@ -359,12 +630,36 @@ const fnProcessing = (type: string, data: any) => {
|
|||||||
* 转账
|
* 转账
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
|
const transferData = ref({})
|
||||||
|
const transferShowDialog = ref(false)
|
||||||
|
const formTransferRef = ref<FormInstance>()
|
||||||
|
const formTransfer = reactive({
|
||||||
|
id: 0,
|
||||||
|
transfer_voucher: '',
|
||||||
|
transfer_remark: ''
|
||||||
|
})
|
||||||
|
const formTransferRules = computed(() => {
|
||||||
|
return {
|
||||||
|
transfer_voucher: [
|
||||||
|
{ required: true, message: t('transferVoucherPlaceholder'), trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleTransfer = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
transferFn(formTransfer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
const transferFn = (data:any) => {
|
const transferFn = (data:any) => {
|
||||||
memberTransfer({ ...data }).then(res => {
|
memberTransfer({ ...data }).then(res => {
|
||||||
auditFailure.value = { refuse_reason: '', id: 0, action: 0 }
|
transferShowDialog.value = false
|
||||||
loadOrderList()
|
loadOrderList()
|
||||||
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
transferShowDialog.value = false
|
||||||
loadOrderList()
|
loadOrderList()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -398,13 +693,24 @@ const detailFn = (id:any) => {
|
|||||||
* 提现审核
|
* 提现审核
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const auditPassShowDialog = ref(false)
|
||||||
|
const curData = ref<any>({})
|
||||||
|
const handlePass = () => {
|
||||||
|
const obj = {
|
||||||
|
id: curData.value.id,
|
||||||
|
action: 'agree'
|
||||||
|
}
|
||||||
|
cashOutAuditFn(obj)
|
||||||
|
}
|
||||||
const cashOutAuditFn = (data:any) => {
|
const cashOutAuditFn = (data:any) => {
|
||||||
memberAudit({
|
memberAudit({
|
||||||
...data
|
...data
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
|
auditPassShowDialog.value = false
|
||||||
loadOrderList()
|
loadOrderList()
|
||||||
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
auditPassShowDialog.value = false
|
||||||
loadOrderList()
|
loadOrderList()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -417,6 +723,41 @@ const confirm = () => {
|
|||||||
cashOutAuditFn(auditFailure.value)
|
cashOutAuditFn(auditFailure.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
|
||||||
|
const formRemarkRef = ref<FormInstance>()
|
||||||
|
const remarkShowDialog = ref(false)
|
||||||
|
const formData = reactive({
|
||||||
|
id: 0,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
const formRemarkRules = computed(() => {
|
||||||
|
return {
|
||||||
|
remark: [
|
||||||
|
{ required: true, message: t('remarkPlaceholder'), trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const handleRemark = (data: any) => {
|
||||||
|
formData.id = data.id
|
||||||
|
formData.remark = ''
|
||||||
|
remarkShowDialog.value = true
|
||||||
|
}
|
||||||
|
const save = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
memberRemark(formData).then((res: any) => {
|
||||||
|
loadOrderList()
|
||||||
|
remarkShowDialog.value = false
|
||||||
|
}).catch(() => {
|
||||||
|
remarkShowDialog.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 会员详情
|
* 会员详情
|
||||||
*/
|
*/
|
||||||
@ -424,6 +765,30 @@ const toMember = (memberId: number) => {
|
|||||||
router.push(`/member/detail?id=${memberId}`)
|
router.push(`/member/detail?id=${memberId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检测打款进度
|
||||||
|
const checkFn = (id: number) => {
|
||||||
|
memberCheck(id).then(res => {
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.table-top :deep(.el-table__body-wrapper) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
--el-table-row-hover-bg-color: var(--el-transfer-border-color);
|
||||||
|
}
|
||||||
|
.remark{
|
||||||
|
&::after{
|
||||||
|
content: '';
|
||||||
|
border-bottom: solid 1px var(--el-border-color-light);
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -10px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -154,6 +154,6 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.member-detail-drawer{
|
.member-detail-drawer{
|
||||||
width: 1000px !important;
|
width: 1300px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="bg-[#F7F9FA] min-h-screen">
|
||||||
<div class="flex justify-between items-center py-[24px] pl-[62px] pr-[64px] home-head">
|
<div class="flex justify-between items-center py-[24px] pl-[62px] pr-[64px] home-head">
|
||||||
<div class="flex items-center" v-if="webConfig">
|
<div class="flex items-center" v-if="webConfig">
|
||||||
<img class="w-[32x] h-[32px] rounded-full" v-if="webConfig.icon" :src="img(webConfig.icon)" alt="">
|
<img class="w-[32x] h-[32px] rounded-full" v-if="webConfig.icon" :src="img(webConfig.icon)" alt="">
|
||||||
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-[1200px] m-auto mt-[62px]">
|
<div class="w-[1400px] m-auto mt-[62px]">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<span class="text-[24px] font-bold">站点列表</span>
|
<span class="text-[24px] font-bold">站点列表</span>
|
||||||
<el-button type="primary" class="w-[90px] !h-[34px]" :disabled="siteGroupLoading" @click="handleChick">创建站点</el-button>
|
<el-button type="primary" class="w-[90px] !h-[34px]" :disabled="siteGroupLoading" @click="handleChick">创建站点</el-button>
|
||||||
@ -20,8 +20,8 @@
|
|||||||
<div class="flex justify-between items-center mt-[18px]">
|
<div class="flex justify-between items-center mt-[18px]">
|
||||||
<div class="w-[800px] text-[14px] whitespace-nowrap">
|
<div class="w-[800px] text-[14px] whitespace-nowrap">
|
||||||
<el-scrollbar :always="true">
|
<el-scrollbar :always="true">
|
||||||
<span :class="['px-[10px] cursor-pointer h-[35px] leading-[35px] inline-block', {'text-[var(--el-color-primary)]': params.app == ''}]" @click="cutAppFn('')">所有应用</span>
|
<span :class="['px-[10px] cursor-pointer h-[35px] leading-[35px] inline-block', {'class-select text-[var(--el-color-primary)]': params.app == ''}]" @click="cutAppFn('')">所有应用</span>
|
||||||
<span :class="['px-[10px] cursor-pointer h-[35px] leading-[35px] inline-block', {'text-[var(--el-color-primary)]': params.app == item.key}]" @click="cutAppFn(item.key)" v-for="(item,index) in addonList" :key="index">{{item.title}}</span>
|
<span :class="['px-[10px] cursor-pointer h-[35px] leading-[35px] inline-block', {'class-select text-[var(--el-color-primary)]': params.app == item.key}]" @click="cutAppFn(item.key)" v-for="(item,index) in addonList" :key="index">{{item.title}}</span>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<el-input v-model.trim="params.keywords" class="!w-[300px] !h-[34px]" placeholder="请输入要搜索的站点名称/编号" @keyup.enter.native="getHomeSiteFn()">
|
<el-input v-model.trim="params.keywords" class="!w-[300px] !h-[34px]" placeholder="请输入要搜索的站点名称/编号" @keyup.enter.native="getHomeSiteFn()">
|
||||||
@ -35,29 +35,29 @@
|
|||||||
|
|
||||||
<div class="min-h-[580px]">
|
<div class="min-h-[580px]">
|
||||||
<div class="flex flex-wrap mt-[30px]" v-loading="loading">
|
<div class="flex flex-wrap mt-[30px]" v-loading="loading">
|
||||||
<div v-for="(item, index) in tableData" :key="index" @click="selectSite(item)" :class="['home-item w-[285px] box-border mb-[20px] cursor-pointer',{'mr-[20px]': index ==0 || (index+1)%4 != 0}]">
|
<div v-for="(item, index) in tableData" :key="index" @click="selectSite(item)" :class="['home-item w-[327px] box-border mb-[30px] cursor-pointer',{'mr-[30px]': index ==0 || (index+1)%4 != 0}]">
|
||||||
<div class="flex items-center px-[24px] pt-[22px] pb-[16px] bg-[#F0F2F4] home-item-head relative">
|
<div class="flex items-center px-[24px] pt-[22px] pb-[10px] home-item-head relative">
|
||||||
<div class="absolute h-[5px] w-full z-1 left-0 top-0" :style="{'background-color': item.theme_color}" v-if="item.theme_color"></div>
|
<div class="absolute h-[4px] w-full z-1 left-0 top-0" :style="{'background-color': item.theme_color}" v-if="item.theme_color"></div>
|
||||||
<img v-if="item.icon" class="w-[48px] h-[48px] mr-[15px] rounded-[50%] overflow-hidden" :src="img(item.icon)" />
|
<img v-if="item.icon" class="w-[46px] h-[46px] mr-[15px] img-shadow rounded-[6px] overflow-hidden" :src="img(item.icon)" />
|
||||||
<img v-else class="w-[48px] h-[48px] mr-[15px] rounded-[50%] overflow-hidden" src="@/app/assets/images/member_head.png" />
|
<img v-else class="w-[46px] h-[46px] mr-[15px] rounded-[6px] img-shadow overflow-hidden" src="@/app/assets/images/site_default.png" />
|
||||||
<div class="flex flex-col flex-1 justify-center">
|
<div class="flex flex-col flex-1 justify-center">
|
||||||
<div class="flex items-center flex-wrap">
|
<div class="flex items-center flex-wrap">
|
||||||
<span class="text-[16px] text-[#000] max-w-[145px] font-bold truncate mr-[10px]">{{item.site_name}}</span>
|
<span class="text-[16px] text-[#000] max-w-[160px] font-bold truncate mr-[10px]">{{item.site_name}}</span>
|
||||||
<div class="flex items-center justify-center min-w-[42px] h-[18px] bg-[#FF5500] rounded-tl-md rounded-br-md items-tab" v-if="item.app_name">
|
<div class="flex items-center justify-center min-w-[42px] h-[18px] bg-[#FF5500] rounded-tl-md rounded-br-md items-tab" v-if="item.app_name">
|
||||||
<span class="text-[12px] text-[#fff]">{{item.app_name}}</span>
|
<span class="text-[12px] text-[#000]">{{item.app_name}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[12px] mt-[3px] text-[#555]">{{item.create_time ? item.create_time.split(" ")[0] : '--'}} 到 {{item.expire_time ? item.expire_time.split(" ")[0] : '--'}}</span>
|
<span class="text-[12px] mt-[3px] text-[#666]">{{item.create_time ? item.create_time.split(" ")[0] : '--'}} 到 {{item.expire_time ? item.expire_time.split(" ")[0] : '--'}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-[24px] py-[20px] text-[#6D7278]">
|
<div class="px-[24px] py-[20px] text-[#666]">
|
||||||
<p class="text-[14px]">站点编号:{{item.site_id}}</p>
|
<p class="text-[14px]">站点编号:{{item.site_id}}</p>
|
||||||
<p class="text-[14px] mt-[2px]">站点套餐:{{item.group_name || '--'}}</p>
|
<p class="text-[14px] mt-[2px]">站点套餐:{{item.group_name || '--'}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!tableData.length && !loading" class="m-auto mt-[100px]">
|
<div v-if="!tableData.length && !loading" class="m-auto mt-[100px]">
|
||||||
<img src="@/app/assets/images/site_empty.png"/>
|
<img src="@/app/assets/images/site_empty.png" class="w-[220px] h-[165px]"/>
|
||||||
<p class="text-center text-gray-400 mt-[20px]">暂无站点</p>
|
<p class="text-center text-gray-400 text-[14px] mt-[20px]">暂无站点</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -284,7 +284,21 @@ watch(() => createSiteDialog.value, () => {
|
|||||||
:deep(.el-input__wrapper) {
|
:deep(.el-input__wrapper) {
|
||||||
@apply rounded-none;
|
@apply rounded-none;
|
||||||
}
|
}
|
||||||
|
.class-select {
|
||||||
|
position: relative;
|
||||||
|
// font-weight: bold;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 2px;
|
||||||
|
height: 2px; /* 下划线的高度 */
|
||||||
|
background-color: var(--el-color-primary); /* 下划线颜色 */
|
||||||
|
width: 75%;
|
||||||
|
left: 12%;
|
||||||
|
}
|
||||||
|
}
|
||||||
.border-color {
|
.border-color {
|
||||||
border-color: var(--el-color-primary);
|
border-color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
@ -293,26 +307,32 @@ watch(() => createSiteDialog.value, () => {
|
|||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
.home-item{
|
.home-item{
|
||||||
box-shadow: 0 2px 4px 0 rgba(161,167,183,0.18);
|
// box-shadow: 0 2px 4px 0 rgba(161,167,183,0.18);
|
||||||
|
background:#fff;
|
||||||
.items-tab span{
|
.items-tab span{
|
||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.home-item:hover {
|
.home-item:hover {
|
||||||
border-color: var(--el-color-primary);
|
box-shadow: 0px 0px 18px rgba(0,0,0, 0.07);
|
||||||
|
// border-color: var(--el-color-primary);
|
||||||
.title {
|
.title {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
.home-item-head{
|
.home-item-head{
|
||||||
background-color: #A1A7B7;
|
// background-color: #A1A7B7;
|
||||||
span{
|
span{
|
||||||
color: #fff !important;
|
// color: #fff !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.home-head{
|
.home-head{
|
||||||
|
background:#fff;
|
||||||
box-shadow: 0 4px 8px 0 rgba(28,31,55,0.04);
|
box-shadow: 0 4px 8px 0 rgba(28,31,55,0.04);
|
||||||
}
|
}
|
||||||
|
.img-shadow{
|
||||||
|
box-shadow: 0px 0px 4px rgba(0,0,0, 0.07);
|
||||||
|
}
|
||||||
.creatBg{
|
.creatBg{
|
||||||
background: url('@/app/assets/images/creatBg.png');
|
background: url('@/app/assets/images/creatBg.png');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|||||||
@ -1,41 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div v-loading="loading" >
|
||||||
<el-card class="box-card !border-none" shadow="never">
|
<el-card class="box-card !border-none" shadow="never">
|
||||||
<div class="p-[30px] box-border border-[1px] border-[var(--el-border-color)] border-solid bg-[var(--el-bg-color)]">
|
<div class="box-border">
|
||||||
<el-card class="box-card !border-none profile-data" shadow="never">
|
<el-card class="box-card !border-none profile-data" shadow="never">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header mb-[20px] w-full">
|
<div class="border-none w-full">
|
||||||
<span class="text-[18px]">{{ t("dataSummarize") }}</span>
|
<span class="text-[16px]">{{ t("dataSummarize") }}</span>
|
||||||
<span class="text-[12px] text-[#666] leading-[16px] ml-[18px]">更新时间 : </span>
|
<!-- <span class="text-[12px] text-[#666] leading-[16px] ml-[18px]">更新时间 : </span>
|
||||||
<span class="text-[12px] text-[#666] leading-[16px]">{{ time }}</span>
|
<span class="text-[12px] text-[#666] leading-[16px]">{{ time }}</span> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-row :gutter="20" class="mt-[20px] top">
|
<el-row :gutter="20" class="mt-[15px] top">
|
||||||
<el-col>
|
<el-col>
|
||||||
<div @click="toHref('site/list','1')" class="cursor-pointer">
|
<el-card shadow="never" @click="toHref('site/list','1')" class="box-card border cursor-pointer min-w-[180px] first-con">
|
||||||
<el-statistic :value="statInfo.today_data.norma_site_count" >
|
<img class="max-w-[24px] max-h-[24px] mb-[10px]" src="@/app/assets/images/index/site_normal.png" />
|
||||||
|
<el-statistic :value="statInfo.today_data.norma_site_count">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="text-[14px] mb-[9px] text-[#666]">
|
<div class="text-[14px] mb-[15px] text-[#666]">
|
||||||
{{ t("normalSiteSum") }}
|
{{ t("normalSiteSum") }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
<div @click="toHref('site/list','1')" class="cursor-pointer">
|
<el-card shadow="never" @click="toHref('site/list','1')" class="cursor-pointer min-w-[180px] first-con">
|
||||||
<el-statistic :value="statInfo.today_data.week_expire_site_count" >
|
<img class="max-w-[24px] max-h-[24px] mb-[10px]" src="@/app/assets/images/index/site2.png" />
|
||||||
|
<el-statistic :value="statInfo.today_data.week_expire_site_count">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="text-[14px] mb-[9px] text-[#666]">
|
<div class="text-[14px] mb-[15px] text-[#666]">
|
||||||
{{ t("weekExpireSiteCount") }}
|
{{ t("weekExpireSiteCount") }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
<div @click="toHref('site/list','2')" class="cursor-pointer">
|
<el-card shadow="never" @click="toHref('site/list','2')" class="cursor-pointer min-w-[180px] first-con">
|
||||||
|
<img class="max-w-[24px] max-h-[24px] mb-[15px]" src="@/app/assets/images/index/site3.png" />
|
||||||
<el-statistic :value="statInfo.today_data.expire_site_count">
|
<el-statistic :value="statInfo.today_data.expire_site_count">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="text-[14px] mb-[9px] text-[#666]">
|
<div class="text-[14px] mb-[9px] text-[#666]">
|
||||||
@ -43,10 +46,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
<div @click="toHref('/app_manage/app_store','uninstalled')" class="cursor-pointer">
|
<el-card shadow="never" @click="toHref('/app_manage/app_store','uninstalled')" class="cursor-pointer min-w-[180px] first-con">
|
||||||
|
<img class="max-w-[24px] max-h-[24px] mb-[15px]" src="@/app/assets/images/index/not_install.png" />
|
||||||
<el-statistic :value="statInfo.app.app_no_installed_count">
|
<el-statistic :value="statInfo.app.app_no_installed_count">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="text-[14px] mb-[9px] text-[#666]">
|
<div class="text-[14px] mb-[9px] text-[#666]">
|
||||||
@ -54,10 +58,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
<div @click="toHref('/app_manage/app_store','installed')" class="cursor-pointer">
|
<el-card shadow="never" @click="toHref('/app_manage/app_store','installed')" class="cursor-pointer min-w-[180px] first-con">
|
||||||
|
<img class="max-w-[24px] max-h-[24px] mb-[10px]" src="@/app/assets/images/index/install.png" />
|
||||||
<el-statistic :value="statInfo.app.app_installed_count">
|
<el-statistic :value="statInfo.app.app_installed_count">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="text-[14px] mb-[9px] text-[#666]">
|
<div class="text-[14px] mb-[9px] text-[#666]">
|
||||||
@ -65,54 +70,85 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<div class="text-[16px] mt-[20px] mb-[15px]">{{ t("常用功能") }}</div>
|
||||||
|
<el-card class="box-card border" shadow="never">
|
||||||
|
<div class="flex justify-between" >
|
||||||
|
<div class="flex-1 h-[125px] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('site/list')">
|
||||||
|
<img class="w-[64px] h-[64px] mb-[5px]" src="@/app/assets/images/index/site_list.png" />
|
||||||
|
<span class="text-[14px]">{{ t("siteList") }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 h-[125px] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('site/group')">
|
||||||
|
<img class="w-[64px] h-[64px] mb-[5px]" src="@/app/assets/images/index/site_tc.png" />
|
||||||
|
<span class="text-[14px]">{{ t("sitePackage") }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 h-[125px] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('site/list')">
|
||||||
|
<img class="w-[64px] h-[64px] mb-[5px]" src="@/app/assets/images/index/site_add.png" />
|
||||||
|
<span class="text-[14px]">{{ t("newSite") }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 h-[125px] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('/admin/site/user')">
|
||||||
|
<img class="w-[64px] h-[64px] mb-[5px]" src="@/app/assets/images/index/site_user.png" />
|
||||||
|
<span class="text-[14px]">{{ t("administrator") }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 h-[125px] flex justify-center flex-col items-center cursor-pointer" @click="toApplication">
|
||||||
|
<img class="w-[64px] h-[64px] mb-[5px]" src="@/app/assets/images/index/app_store.png" />
|
||||||
|
<span class="text-[14px]">{{ t("appMarketplace") }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
<div class="flex justify-between mt-[15px]">
|
|
||||||
<div class="flex-1 h-[145px] bg-[var(--el-color-info-light-9)] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('site/list')">
|
<div class="mt-[20px] flex site">
|
||||||
<img class="max-w-[40px] max-h-[40px] mb-[5px]" src="@/app/assets/images/index/site1.png" />
|
<div class="flex-1 ">
|
||||||
<span class="text-[16px]">{{ t("siteList") }}</span>
|
<div class="text-[16px] mb-[15px]">{{ t("newSite") }}</div>
|
||||||
|
<el-card class="box-card border profile-data mr-[30px]" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<div ref="newSiteStat" class="echarts-con" :style="{ width: '100%', height: '320px' }"></div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 h-[145px] bg-[var(--el-color-info-light-9)] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('site/group')">
|
<div class="flex-1 ">
|
||||||
<img class="max-w-[40px] max-h-[40px] mb-[5px]" src="@/app/assets/images/index/site_class1.png" />
|
<div class="text-[16px] mb-[15px]">{{ t("addUser") }}</div>
|
||||||
<span class="text-[16px]">{{ t("sitePackage") }}</span>
|
<el-card class="box-card border flex-1 profile-data" shadow="never">
|
||||||
</div>
|
<template #header>
|
||||||
<div class="flex-1 h-[145px] bg-[var(--el-color-info-light-9)] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('site/list')">
|
</template>
|
||||||
<img class="max-w-[40px] max-h-[40px] mb-[5px]" src="@/app/assets/images/index/new_site1.png" />
|
<div ref="addUser" class="echarts-con" :style="{ width: '100%', height: '320px' }"></div>
|
||||||
<span class="text-[16px]">{{ t("newSite") }}</span>
|
</el-card>
|
||||||
</div>
|
|
||||||
<div class="flex-1 h-[145px] bg-[var(--el-color-info-light-9)] flex justify-center flex-col items-center cursor-pointer mr-[25px]" @click="toLink('/admin/site/user')">
|
|
||||||
<img class="max-w-[40px] max-h-[40px] mb-[5px]" src="@/app/assets/images/index/auth1.png" />
|
|
||||||
<span class="text-[16px]">{{ t("administrator") }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1 h-[145px] bg-[var(--el-color-info-light-9)] flex justify-center flex-col items-center cursor-pointer" @click="toApplication">
|
|
||||||
<img class="max-w-[40px] max-h-[40px] mb-[5px]" src="@/app/assets/images/index/app1.png" />
|
|
||||||
<span class="text-[16px]">{{ t("appMarketplace") }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex justify-between text-[12px] mt-[20px] text-[#666]" v-if="copyright">
|
||||||
<div class="mt-[60px] flex site">
|
<div>
|
||||||
<el-card class="box-card !border-none flex-1 profile-data mr-[30px]" shadow="never">
|
<a :href="copyright.copyright_link" target="_blank">
|
||||||
<template #header>
|
<!-- <span class="mr-3" v-if="copyright.copyright_desc">{{ copyright.copyright_desc }}</span> -->
|
||||||
<div class="card-header mb-[20px]">
|
<span class="mr-3" v-if="copyright.company_name">{{ copyright.company_name }}</span>
|
||||||
<span class="text-[18px]">{{ t("newSite") }}</span>
|
</a>
|
||||||
</div>
|
<a href="https://beian.miit.gov.cn/" v-if="copyright.icp" target="_blank">
|
||||||
</template>
|
<span class="mr-3">备案号: {{ copyright.icp }}</span>
|
||||||
<div ref="newSiteStat" class="mt-[20px]" :style="{ width: '100%', height: '300px' }"></div>
|
</a>
|
||||||
</el-card>
|
</div>
|
||||||
<el-card class="box-card !border-none flex-1 profile-data" shadow="never">
|
<div class="flex items-center">
|
||||||
<template #header>
|
<span class="mx-1" @click="getInfoFn">版权信息</span>
|
||||||
<div class="card-header mb-[20px]">
|
<!-- | <span class="mx-1">隐私政策</span> | <span class="mx-1">联系我们</span> -->
|
||||||
<span class="text-[18px]">{{ t("addUser") }}</span>
|
<!-- 版权信息 | 开发者联盟与隐私的声明 | 隐私政策 | 联系我们 | Cookies -->
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div ref="addUser" class="mt-[20px]" :style="{ width: '100%', height: '300px' }"></div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<el-dialog v-model="dialogVisible" title="版权信息" width="500">
|
||||||
|
<span>{{ copyright.copyright_desc }}</span>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="dialogVisible = false">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -122,15 +158,23 @@ import { t } from '@/lang'
|
|||||||
import { getStatInfo } from '@/app/api/stat'
|
import { getStatInfo } from '@/app/api/stat'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import { getFrameworkNewVersion } from '@/app/api/module'
|
import { getFrameworkNewVersion } from '@/app/api/module'
|
||||||
|
import { getWebCopyright } from '@/app/api/sys'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { AnyObject } from '@/types/global'
|
import { AnyObject } from '@/types/global'
|
||||||
import useStyleStore from '@/stores/modules/style'
|
import useStyleStore from '@/stores/modules/style'
|
||||||
|
import Storage from '@/utils/storage'
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const newSiteStat = ref<any>(null)
|
const newSiteStat = ref<any>(null)
|
||||||
const addUser = ref<any>(null)
|
const addUser = ref<any>(null)
|
||||||
const styleStore = useStyleStore()
|
const styleStore = useStyleStore()
|
||||||
|
const currLayout = ref(Storage.get('admin_layout') || 'admin')
|
||||||
|
|
||||||
|
const copyright = ref(null)
|
||||||
|
getWebCopyright().then(({ data }) => {
|
||||||
|
copyright.value = data
|
||||||
|
})
|
||||||
interface NewVersion{
|
interface NewVersion{
|
||||||
last_version: string
|
last_version: string
|
||||||
}
|
}
|
||||||
@ -149,6 +193,12 @@ const newVersion = ref<NewVersion>({
|
|||||||
last_version: ''
|
last_version: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getInfoFn = () => {
|
||||||
|
if(copyright.value.copyright_desc){
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getFrameworkNewVersion().then(({ data }) => {
|
getFrameworkNewVersion().then(({ data }) => {
|
||||||
newVersion.value = data
|
newVersion.value = data
|
||||||
})
|
})
|
||||||
@ -190,8 +240,17 @@ const drawChart = () => {
|
|||||||
{
|
{
|
||||||
name: t('newSite'),
|
name: t('newSite'),
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: []
|
data: [],
|
||||||
}
|
itemStyle:{
|
||||||
|
normal:{
|
||||||
|
color:'#2FCEB6',//点的颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineStyle:{
|
||||||
|
color:'#2FCEB6',//线的颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
newSiteStatOption.value.xAxis.data = statInfo.value.site_stat.date
|
newSiteStatOption.value.xAxis.data = statInfo.value.site_stat.date
|
||||||
@ -213,7 +272,15 @@ const drawChart = () => {
|
|||||||
{
|
{
|
||||||
name: t('addUser'),
|
name: t('addUser'),
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: []
|
data: [],
|
||||||
|
itemStyle:{
|
||||||
|
normal:{
|
||||||
|
color:'#F7DC76',//点的颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineStyle:{
|
||||||
|
color:'#F7DC76',//线的颜色
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@ -284,12 +351,26 @@ nowTime()
|
|||||||
|
|
||||||
:deep(.profile-data .el-card__header) {
|
:deep(.profile-data .el-card__header) {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
border: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.profile-data .el-card__body) {
|
:deep(.profile-data .el-card__body) {
|
||||||
padding: 20px 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
.top :deep(.el-col){
|
.top :deep(.el-col){
|
||||||
max-width: calc(100% / 5) !important;
|
max-width: calc(100% / 5) !important;
|
||||||
}
|
}
|
||||||
|
.first-con{
|
||||||
|
// border: 1px solid #E9ECEF;
|
||||||
|
// background: #fff;
|
||||||
|
padding: 20px 30px 10px;
|
||||||
|
height: 160px;
|
||||||
|
// border-radius: 8px;
|
||||||
|
}
|
||||||
|
.echarts-con{
|
||||||
|
// border: 1px solid #E9ECEF;
|
||||||
|
// background: #fff;
|
||||||
|
padding-top:20px;
|
||||||
|
// border-radius: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -17,13 +17,13 @@
|
|||||||
|
|
||||||
<div class="flex justify-between my-[20px]">
|
<div class="flex justify-between my-[20px]">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'installed' }]" @click="activeNameTabFn('installed')">
|
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'installed' }]" @click="activeNameTabFn('installed')">
|
||||||
{{ t('installLabel') }}
|
{{ t('installLabel') }}
|
||||||
</div>
|
</div>
|
||||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'uninstalled' }]" @click="activeNameTabFn('uninstalled')">
|
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'uninstalled' }]" @click="activeNameTabFn('uninstalled')">
|
||||||
{{ t('uninstalledLabel') }}
|
{{ t('uninstalledLabel') }}
|
||||||
</div>
|
</div>
|
||||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'all' }]" @click="activeNameTabFn('all')">
|
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'all' }]" @click="activeNameTabFn('all')">
|
||||||
{{ t('buyLabel') }}
|
{{ t('buyLabel') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<el-table v-if="localList[activeName].length&&!loading" :data="info[activeName]" size="large" class="pt-[5px]">
|
<el-table v-if="localList[activeName].length&&!loading" :data="info[activeName]" size="large" class="pt-[5px]">
|
||||||
<el-table-column :label="t('appName')" align="left" width="320">
|
<el-table-column :label="t('appName')" align="left" width="450">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="flex items-center cursor-pointer" @click = "handleTips">
|
<div class="flex items-center cursor-pointer" @click = "handleTips">
|
||||||
<el-image class="w-[54px] h-[54px]" :src="row.icon" fit="contain">
|
<el-image class="w-[54px] h-[54px]" :src="row.icon" fit="contain">
|
||||||
@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="left" min-width="120">
|
<el-table-column align="left" min-width="150">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-500 text-[13px] mr-[5px]">{{ t('appIdentification') }}</span>
|
<span class="font-500 text-[13px] mr-[5px]">{{ t('appIdentification') }}</span>
|
||||||
@ -76,12 +76,12 @@
|
|||||||
<span class="font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
|
<span class="font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="t('type')" align="left" min-width="100">
|
<el-table-column :label="t('type')" align="left" min-width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="font-500 text-[13px]">{{ row.type === 'app' ? t('app') : t('addon') }}</span>
|
<span class="font-500 text-[13px]">{{ row.type === 'app' ? t('app') : t('addon') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="" :label="t('author')" align="left" min-width="100">
|
<el-table-column prop="" :label="t('author')" align="left" min-width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="font-500 text-[13px]">{{ row.author }}</span>
|
<span class="font-500 text-[13px]">{{ row.author }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -30,49 +30,46 @@
|
|||||||
</el-main>
|
</el-main>
|
||||||
|
|
||||||
<!-- 站点端登录 -->
|
<!-- 站点端登录 -->
|
||||||
<el-main class="login-main w-full login-site-main items-center h-screen justify-evenly bg-[#F8FAFF]" v-else-if="!imgLoading && loginType == 'site'">
|
<el-main class="login-main w-full mt-[120px] justify-center h-0" v-else-if="!imgLoading && loginType == 'site'">
|
||||||
<div class="flex overflow-hidden h-screen w-full relative">
|
<div>
|
||||||
<template v-if="loginConfig">
|
<div class="login-main-left flex flex-col items-center justify-center">
|
||||||
<img v-if="loginConfig.site_bg&&!imgLoading" class="hidden h-[100%] lg:block" :src="img(loginConfig.site_bg)" />
|
<div class="w-[130px] h-[40px] overflow-hidden" v-if="webSite.icon">
|
||||||
<img v-else class="hidden h-[100%] lg:block" src="@/app/assets/images/login/site_login_bg.png" />
|
<el-image class="w-full h-full" :src="img(webSite.icon)" fit="contain">
|
||||||
</template>
|
<template #error>
|
||||||
<div class="w-[100%] lg:w-[60%] h-screen flex flex-col absolute right-0 top-0 bg-[#F8FAFF]">
|
<div class="flex justify-center items-center w-full h-full"><img class="max-w-[130px]" src="@/app/assets/images/logo.default.png" alt="" object-fit="contain"></div>
|
||||||
<div class="flex justify-center items-center flex-1 h-0">
|
</template>
|
||||||
<div class="site-login-item w-[400px] h-[380px] p-[40px] rounded-2xl shadow bg-[#fff]">
|
</el-image>
|
||||||
<h3 class="text-3xl mb-[30px]">{{ t('siteLogin') }}</h3>
|
|
||||||
<el-form :model="form" ref="formRef" :rules="formRules">
|
|
||||||
<el-form-item prop="username">
|
|
||||||
<el-input v-model.trim="form.username" @keyup.enter="handleLogin(formRef)" autocomplete="off" class="h-[40px]" :placeholder="t('userPlaceholder')"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item prop="password" class="mt-[30px]">
|
|
||||||
<el-input type="password" v-model.trim="form.password" @keyup.enter="handleLogin(formRef)" autocomplete="new-password" :show-password="true" class="h-[40px]" :placeholder="t('passwordPlaceholder')"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" class="mt-[30px] !h-[40px] w-full" @click="handleLogin(formRef)" :loading="loading">{{ loading ? t('logging') : t('login') }}</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-[30px] mt-[10px]">{{ webSite.site_name || t('siteTitle') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="login flex flex-col w-[400px] mt-[60px] h-[350px] rounded-[10px] py-[20px] px-[40px]">
|
||||||
|
<h3 class="text-center mt-[20px] text-[24px]">{{ t('欢迎登录') }}</h3>
|
||||||
|
<el-form :model="form" ref="formRef" :rules="formRules" class="mt-[30px] formtwo">
|
||||||
|
<el-form-item prop="username">
|
||||||
|
<el-input v-model.trim="form.username" :placeholder="t('userPlaceholder')" autocomplete="off" :input-style="{boxShadow: 'none'}" @keyup.enter="handleLogin(formRef)" class="h-[40px]">
|
||||||
|
<template #prefix>
|
||||||
|
<img class="max-w-[20px]" src="@/app/assets/images/login/username.png" alt="" object-fit="contain">
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<div class="flex items-center justify-center mt-[20px] pb-[20px] text-sm text-[#999]" v-if="copyright">
|
<el-form-item prop="password" class="mt-[30px]">
|
||||||
<a :href="copyright.copyright_link" target="_blank">
|
<el-input v-model.trim="form.password" :placeholder="t('passwordPlaceholder')" type="password" autocomplete="new-password" @keyup.enter="handleLogin(formRef)" :show-password="true" class="h-[40px]">
|
||||||
<span class="mr-3" v-if="copyright.copyright_desc">{{ copyright.copyright_desc }}</span>
|
<template #prefix>
|
||||||
<span class="mr-3" v-if="copyright.company_name">{{ copyright.company_name }}</span>
|
<img class="max-w-[20px]" src="@/app/assets/images/login/password.png" alt="" object-fit="contain">
|
||||||
</a>
|
</template>
|
||||||
<a href="https://beian.miit.gov.cn/" v-if="copyright.icp" target="_blank">
|
</el-input>
|
||||||
<span class="mr-3">{{ copyright.icp }}</span>
|
</el-form-item>
|
||||||
</a>
|
<el-form-item>
|
||||||
<a :href="copyright.gov_url" v-if="copyright.gov_record" target="_blank">
|
<el-button type="primary" class="mt-[30px] !h-[40px] w-full" @click="handleLogin(formRef)" :loading="loading">{{ loading ? t('logging') : t('login') }}</el-button>
|
||||||
<span class="mr-3">{{ copyright.gov_record }}</span>
|
</el-form-item>
|
||||||
</a>
|
</el-form>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-main>
|
</el-main>
|
||||||
|
|
||||||
<div class="flex items-center justify-center mt-[20px] pb-[20px] text-sm text-[#999]" v-if="copyright && loginType == 'admin'">
|
<div class="flex items-center justify-center mt-[20px] pb-[20px] text-sm text-[#666]" v-if="copyright">
|
||||||
<a :href="copyright.copyright_link" target="_blank">
|
<a :href="copyright.copyright_link" target="_blank">
|
||||||
<span class="mr-3" v-if="copyright.copyright_desc">{{ copyright.copyright_desc }}</span>
|
<span class="mr-3" v-if="copyright.copyright_desc">{{ copyright.copyright_desc }}</span>
|
||||||
<span class="mr-3" v-if="copyright.company_name">{{ copyright.company_name }}</span>
|
<span class="mr-3" v-if="copyright.company_name">{{ copyright.company_name }}</span>
|
||||||
@ -111,14 +108,15 @@ const userStore = useUserStore()
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const copyright = ref(null)
|
const copyright = ref(null)
|
||||||
|
const systemStore = useSystemStore()
|
||||||
|
|
||||||
getWebCopyright().then(({ data }) => {
|
getWebCopyright().then(({ data }) => {
|
||||||
copyright.value = data
|
copyright.value = data
|
||||||
})
|
})
|
||||||
|
|
||||||
route.redirectedFrom && (route.query.redirect = route.redirectedFrom.path)
|
route.redirectedFrom && (route.query.redirect = route.redirectedFrom.path)
|
||||||
|
|
||||||
const webSite = computed(() => useSystemStore().website)
|
const webSite = computed(() => useSystemStore().website)
|
||||||
|
|
||||||
|
|
||||||
// 判断登录页面[平台或者站点]
|
// 判断登录页面[平台或者站点]
|
||||||
const loginType = ref(getAppType())
|
const loginType = ref(getAppType())
|
||||||
|
|
||||||
@ -140,6 +138,7 @@ const getLoginConfigFn = async () => {
|
|||||||
imgLoading.value = true
|
imgLoading.value = true
|
||||||
const data = await (await getLoginConfig()).data
|
const data = await (await getLoginConfig()).data
|
||||||
loginConfig.value = data
|
loginConfig.value = data
|
||||||
|
|
||||||
imgLoading.value = false
|
imgLoading.value = false
|
||||||
}
|
}
|
||||||
getLoginConfigFn()
|
getLoginConfigFn()
|
||||||
@ -188,8 +187,10 @@ const loginFn = (data = {}) => {
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-site-main {
|
.site-login-wrap {
|
||||||
padding: 0 !important;
|
background-image: url("@/app/assets/images/login/login_bg.jpg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-main {
|
.login-main {
|
||||||
@ -202,6 +203,14 @@ const loginFn = (data = {}) => {
|
|||||||
.el-form-item__error {
|
.el-form-item__error {
|
||||||
top : 45px;
|
top : 45px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.formtwo .el-input__wrapper{
|
||||||
|
// border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-bottom: 1px solid #DCDEE0 !important;
|
||||||
|
padding:10px 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 750px) {
|
@media only screen and (max-width: 750px) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form :model="formData" :rules="formRules" class="page-form" ref="formRef">
|
<el-form :model="formData" :rules="formRules" class="page-form" ref="formRef">
|
||||||
<el-form-item :label="t('continueSign')" prop="continue_sign">
|
<el-form-item :label="t('continueSign')" prop="continue_sign">
|
||||||
<el-input class="input-width" v-model.trim="formData.continue_sign" :maxlength="5" clearable />
|
<el-input class="input-width" v-model.trim="formData.continue_sign" @keyup="filterNumber($event)" :maxlength="3" clearable />
|
||||||
<span class="ml-[10px]">{{ t('day') }}</span>
|
<span class="ml-[10px]">{{ t('day') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('continueSign')" >
|
<el-form-item :label="t('continueSign')" >
|
||||||
@ -28,7 +28,7 @@ import { t } from '@/lang'
|
|||||||
import { ref, reactive, defineAsyncComponent, computed, watch } from 'vue'
|
import { ref, reactive, defineAsyncComponent, computed, watch } from 'vue'
|
||||||
import { FormRules } from 'element-plus'
|
import { FormRules } from 'element-plus'
|
||||||
import { getGiftDict } from '@/app/api/member'
|
import { getGiftDict } from '@/app/api/member'
|
||||||
import { guid } from '@/utils/common'
|
import { guid, filterNumber } from '@/utils/common'
|
||||||
import Test from '@/utils/test'
|
import Test from '@/utils/test'
|
||||||
|
|
||||||
const gifts = ref({})
|
const gifts = ref({})
|
||||||
@ -89,12 +89,11 @@ const formRules = reactive<FormRules>({
|
|||||||
continue_sign: [
|
continue_sign: [
|
||||||
{ required: true, message: t('continueSignPlaceholder'), trigger: 'blur' },
|
{ required: true, message: t('continueSignPlaceholder'), trigger: 'blur' },
|
||||||
{
|
{
|
||||||
|
|
||||||
validator: (rule: any, value: any, callback: any) => {
|
validator: (rule: any, value: any, callback: any) => {
|
||||||
if (isNaN(value) || !regExp.number.test(value)) {
|
if (isNaN(value) || !regExp.number.test(value)) {
|
||||||
callback('连续签到天数格式错误')
|
callback(t('continueSignFormatError'))
|
||||||
} else if (value <=0) {
|
} else if (value < 2 || value > 365) {
|
||||||
callback('连续签到天数不能小于等于0')
|
callback(t('continueSignBerweenDays'))
|
||||||
} else{
|
} else{
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -108,13 +107,13 @@ const formRules = reactive<FormRules>({
|
|||||||
validator: (rule: any, value: any, callback: Function) => {
|
validator: (rule: any, value: any, callback: Function) => {
|
||||||
if (formData.value.receive_limit == 2) {
|
if (formData.value.receive_limit == 2) {
|
||||||
if (Test.empty(formData.value.receive_num)) {
|
if (Test.empty(formData.value.receive_num)) {
|
||||||
callback('请输入限领次数')
|
callback(t('receiveNumPlaceholder'))
|
||||||
}
|
}
|
||||||
if (isNaN(formData.value.receive_num) || !regExp.number.test(formData.value.receive_num)) {
|
if (isNaN(formData.value.receive_num) || !regExp.number.test(formData.value.receive_num)) {
|
||||||
callback('限领次数格式错误')
|
callback(t('receiveNumFormatError'))
|
||||||
}
|
}
|
||||||
if (formData.value.receive_num <= 0) {
|
if (formData.value.receive_num <= 0) {
|
||||||
callback('限领次数不能小于等于0')
|
callback(t('receiveNumMustGreaterThanZeroTip'))
|
||||||
}
|
}
|
||||||
callback()
|
callback()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -163,6 +163,6 @@ defineExpose({
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.member-detail-drawer{
|
.member-detail-drawer{
|
||||||
width: 1000px !important;
|
width: 1300px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('signPeriod')" prop="sign_period" v-if="formData.is_use">
|
<el-form-item :label="t('signPeriod')" prop="sign_period" v-if="formData.is_use">
|
||||||
<el-input v-model="formData.sign_period" placeholder="0" maxlength="8" clearable class="input-width" /><span class="ml-[10px]">天</span>
|
<el-input v-model.trim="formData.sign_period" @keyup="filterNumber($event)" maxlength="3" clearable class="input-width" /><span class="ml-[10px]">天</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('daySignAward')" prop="day_award" v-if="formData.is_use">
|
<el-form-item :label="t('daySignAward')" prop="day_award" v-if="formData.is_use">
|
||||||
@ -115,6 +115,7 @@ import { getSignConfig, setSignConfig, getMemberGiftsContent } from '@/app/api/m
|
|||||||
import signDay from '@/app/views/marketing/components/sign-day.vue'
|
import signDay from '@/app/views/marketing/components/sign-day.vue'
|
||||||
import signContinue from '@/app/views/marketing/components/sign-continue.vue'
|
import signContinue from '@/app/views/marketing/components/sign-continue.vue'
|
||||||
import { FormInstance, FormRules } from 'element-plus'
|
import { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import { filterNumber } from '@/utils/common'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ const formRules = reactive<FormRules>({
|
|||||||
callback(t('signPeriodTip'))
|
callback(t('signPeriodTip'))
|
||||||
}else if (isNaN(value) || !regExp.number.test(value)) {
|
}else if (isNaN(value) || !regExp.number.test(value)) {
|
||||||
callback(t('signPeriodLimitTips'))
|
callback(t('signPeriodLimitTips'))
|
||||||
}else if (value <= 0) {
|
}else if (value < 2 || value > 365) {
|
||||||
callback(t('signPeriodMustZeroTips'))
|
callback(t('signPeriodMustZeroTips'))
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
|
|||||||
@ -401,6 +401,6 @@ defineExpose({
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.member-detail-drawer{
|
.member-detail-drawer{
|
||||||
width: 1000px !important;
|
width: 1300px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
<el-form-item :label="t('printWidth')" prop="print_width">
|
<el-form-item :label="t('printWidth')" prop="print_width">
|
||||||
<el-radio-group v-model="formData.print_width">
|
<el-radio-group v-model="formData.print_width">
|
||||||
<el-radio value="58mm">58mm</el-radio>
|
<el-radio label="58mm">58mm</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
|||||||
@ -81,7 +81,7 @@ const formRules = computed(() => {
|
|||||||
validator: (rule: any, value: any, callback: any) => {
|
validator: (rule: any, value: any, callback: any) => {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
callback(new Error(t('contentPlaceholder')))
|
callback(new Error(t('contentPlaceholder')))
|
||||||
} else if (value.length < 5 || value.length > 50000) {
|
} else if (value.length < 5 || value.length > 100000) {
|
||||||
callback(new Error(t('contentMaxTips')))
|
callback(new Error(t('contentMaxTips')))
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -15,7 +15,10 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('cashWithdrawalAmount')" v-if="formData.is_open" prop="min">
|
<el-form-item :label="t('cashWithdrawalAmount')" v-if="formData.is_open" prop="min">
|
||||||
<el-input v-model.trim="formData.min" @keyup="filterDigit($event)" class="input-width" :placeholder="t('cashWithdrawalAmountPlaceholder')" />
|
<div>
|
||||||
|
<el-input v-model.trim="formData.min" @keyup="filterDigit($event)" class="input-width" :placeholder="t('cashWithdrawalAmountPlaceholder')" />
|
||||||
|
<div class="text-[12px] text-[#999] leading-[24px]">{{ t('minTips') }}</div>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('commissionRatio')" v-if="formData.is_open" prop="rate">
|
<el-form-item :label="t('commissionRatio')" v-if="formData.is_open" prop="rate">
|
||||||
@ -30,17 +33,22 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('transfer')" v-if="formData.is_open" class="items-center">
|
<el-form-item :label="t('transfer')" v-if="formData.is_open" class="items-baseline">
|
||||||
<el-radio-group v-model="formData.is_auto_transfer">
|
<div>
|
||||||
<el-radio label="0" size="large">{{t('manualTransfer')}}</el-radio>
|
<el-radio-group v-model="formData.is_auto_transfer">
|
||||||
<el-radio label="1" size="large">{{t('automatedTransit')}}</el-radio>
|
<el-radio label="0" size="large">{{t('manualTransfer')}}</el-radio>
|
||||||
</el-radio-group>
|
<el-radio label="1" size="large">{{t('automatedTransit')}}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<div class="text-[12px] text-[#999] leading-[24px]">{{ t('transferTips') }}</div>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('transferMode')" v-if="formData.is_open" class="items-baseline">
|
||||||
<el-form-item :label="t('transferMode')" v-if="formData.is_open" class="items-center">
|
<div>
|
||||||
<el-checkbox-group v-model="formData.transfer_type" size="large">
|
<el-checkbox-group v-model="formData.transfer_type" size="large">
|
||||||
<el-checkbox :label="item.key" v-for="(item,index) in Transfertype" :key="'a'+index">{{item.name}}</el-checkbox>
|
<el-checkbox :label="item.key" v-for="(item,index) in Transfertype" :key="'a'+index">{{item.name}}</el-checkbox>
|
||||||
</el-checkbox-group>
|
</el-checkbox-group>
|
||||||
|
<div class="text-[12px] text-[#999] leading-[24px]">{{ t('transferModeTips') }}</div>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|||||||
@ -30,6 +30,12 @@
|
|||||||
<el-form-item :label="t('remark')" prop="config.pay_leave_message">
|
<el-form-item :label="t('remark')" prop="config.pay_leave_message">
|
||||||
<el-input v-model.trim="formData.config.pay_leave_message" :placeholder="t('remarkPlaceholder')" class="input-width" type="textarea" rows="4" maxlength="20" show-word-limit clearable />
|
<el-input v-model.trim="formData.config.pay_leave_message" :placeholder="t('remarkPlaceholder')" class="input-width" type="textarea" rows="4" maxlength="20" show-word-limit clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('payWechatImage')" prop="config.pay_wechat_share_image" v-if="initData.redio_key == 'wechat_friendspay'">
|
||||||
|
<upload-image v-model="formData.config.pay_wechat_share_image" :limit="1" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('payWeappImage')" prop="config.pay_weapp_share_image" v-if="initData.redio_key == 'weapp_friendspay'">
|
||||||
|
<upload-image v-model="formData.config.pay_weapp_share_image" :limit="1" />
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
@ -63,7 +69,9 @@ const initialFormData = {
|
|||||||
pay_type_name: '',
|
pay_type_name: '',
|
||||||
pay_page_name: '',
|
pay_page_name: '',
|
||||||
pay_button_name: '',
|
pay_button_name: '',
|
||||||
pay_leave_message: ''
|
pay_leave_message: '',
|
||||||
|
pay_wechat_share_image: '',
|
||||||
|
pay_weapp_share_image: ''
|
||||||
},
|
},
|
||||||
channel: '',
|
channel: '',
|
||||||
status: 0,
|
status: 0,
|
||||||
@ -146,6 +154,7 @@ const cancel = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setFormData = async (data: any = null) => {
|
const setFormData = async (data: any = null) => {
|
||||||
|
console.log(data)
|
||||||
initData.value = cloneDeep(data)
|
initData.value = cloneDeep(data)
|
||||||
loading.value = true
|
loading.value = true
|
||||||
Object.assign(formData, initialFormData)
|
Object.assign(formData, initialFormData)
|
||||||
|
|||||||
@ -40,7 +40,15 @@
|
|||||||
<el-dialog v-model="showDialog" :title="t('selectLayout')" width="800" :destroy-on-close="true">
|
<el-dialog v-model="showDialog" :title="t('selectLayout')" width="800" :destroy-on-close="true">
|
||||||
<div class="h-[300px]">
|
<div class="h-[300px]">
|
||||||
<el-scrollbar >
|
<el-scrollbar >
|
||||||
<h3 class="panel-title !text-sm">{{ t('layout') }}</h3>
|
<div class="flex justify-between items-center mb-[20px]">
|
||||||
|
<h3 class="!text-sm !text-[#444]">{{ t('layout') }}</h3>
|
||||||
|
<div class="flex items-center cursor-pointer" @click="toDiyLayout">
|
||||||
|
<span class="iconfont iconwenhao text-[#999] !text-[14px]"></span>
|
||||||
|
<div class="ml-[2px] text-[12px] text-[#999]">如何开发自定义布局</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-items-stretch">
|
<div class="flex justify-items-stretch">
|
||||||
<div class="w-[180px] h-[130px] mr-[10px] mb-[10px] border hover:border-primary cursor-pointer"
|
<div class="w-[180px] h-[130px] mr-[10px] mb-[10px] border hover:border-primary cursor-pointer"
|
||||||
:class="{'border-primary': ((!layoutConfig[currAddon] && item.layout == 'default') || (layoutConfig[currAddon] == item.layout)) }"
|
:class="{'border-primary': ((!layoutConfig[currAddon] && item.layout == 'default') || (layoutConfig[currAddon] == item.layout)) }"
|
||||||
@ -56,8 +64,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="panel-title !text-sm">{{ t('themeColor') }}</h3>
|
<h3 class="panel-title !text-sm">{{ t('themeColor') }}</h3>
|
||||||
<div class="flex justify-items-stretch">
|
<div class="">
|
||||||
<el-color-picker v-model="themeColor[currAddon]" size="large" />
|
<el-color-picker v-model="themeColor[currAddon]" size="large" />
|
||||||
|
<div class="form-tip text-[#999] mt-2">设置的色调会在前端站点列表体现</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
@ -142,6 +151,13 @@ const confirm = () => {
|
|||||||
})
|
})
|
||||||
showDialog.value = false
|
showDialog.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 跳转自定义布局
|
||||||
|
const toDiyLayout = () => {
|
||||||
|
let url = 'https://doc.niucloud.com/saas.html?keywords=/ru-he-kai-fa-zi-ding-yi-bu-ju-hou-tai-bu-ju';
|
||||||
|
window.open(url)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -134,7 +134,6 @@ const setConfigInfo = (data:any) => {
|
|||||||
element.config = data.config
|
element.config = data.config
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
console.log(payConfigData.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化配置信息
|
// 初始化配置信息
|
||||||
|
|||||||
@ -8,7 +8,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-[20px]">
|
<div class="mt-[20px]">
|
||||||
<el-alert :title="t('operationTip')" type="warning" show-icon />
|
<el-alert :description="t('transferTips')" type="warning" show-icon>
|
||||||
|
<template #title>
|
||||||
|
<span class="!text-[14px]">{{ t('operationTip') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
@ -38,7 +42,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card class="box-card mt-[15px] !border-none" shadow="never">
|
<!-- <el-card class="box-card mt-[15px] !border-none" shadow="never">
|
||||||
<h3 class="panel-title !text-sm">{{t('alipay')}}</h3>
|
<h3 class="panel-title !text-sm">{{t('alipay')}}</h3>
|
||||||
|
|
||||||
<el-form-item :label="t('appId')" prop="alipay_config.app_id">
|
<el-form-item :label="t('appId')" prop="alipay_config.app_id">
|
||||||
@ -63,7 +67,7 @@
|
|||||||
<upload-file v-model="formData.alipay_config.alipay_root_cert_path" api="sys/document/aliyun" />
|
<upload-file v-model="formData.alipay_config.alipay_root_cert_path" api="sys/document/aliyun" />
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-card>
|
</el-card> -->
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<div class="fixed-footer-wrap">
|
<div class="fixed-footer-wrap">
|
||||||
|
|||||||
@ -96,8 +96,8 @@
|
|||||||
|
|
||||||
<el-table-column prop="group_name" :label="t('groupId')" width="150" :show-overflow-tooltip="true" />
|
<el-table-column prop="group_name" :label="t('groupId')" width="150" :show-overflow-tooltip="true" />
|
||||||
<el-table-column prop="site_domain" :label="t('siteDomain')" width="150" :show-overflow-tooltip="true" />
|
<el-table-column prop="site_domain" :label="t('siteDomain')" width="150" :show-overflow-tooltip="true" />
|
||||||
<el-table-column prop="create_time" :label="t('createTime')" width="250" :show-overflow-tooltip="true" />
|
<el-table-column prop="create_time" :label="t('createTime')" width="200" :show-overflow-tooltip="true" />
|
||||||
<el-table-column prop="expire_time" :label="t('expireTime')" width="250" :show-overflow-tooltip="true">
|
<el-table-column prop="expire_time" :label="t('expireTime')" width="200" :show-overflow-tooltip="true">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div v-if="row.expire_time == 0">永久</div>
|
<div v-if="row.expire_time == 0">永久</div>
|
||||||
<div v-else>{{ row.expire_time }}</div>
|
<div v-else>{{ row.expire_time }}</div>
|
||||||
|
|||||||
@ -126,7 +126,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex absolute top-0 left-0 right-0 bottom-0 items-center justify-center" v-else>
|
<div class="flex absolute top-0 left-0 right-0 bottom-0 items-center justify-center" v-else>
|
||||||
<div class="flex flex-col items-center" v-if="!attachment.loading">
|
<div class="flex flex-col items-center" v-if="!attachment.loading">
|
||||||
<img src="@/app/assets/images/no_attachment.png" class="max-w-[130px] max-h-[130px] mb-[15px]">
|
<img src="@/app/assets/images/no_attachment.png" class="max-w-[160px] max-h-[120px] mb-[15px]">
|
||||||
<span class="text-[var(--el-text-color-secondary)] text-[14px]">{{type == 'icon' ? t('upload.iconEmpty') : t('upload.attachmentEmpty')}}</span>
|
<span class="text-[var(--el-text-color-secondary)] text-[14px]">{{type == 'icon' ? t('upload.iconEmpty') : t('upload.attachmentEmpty')}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,37 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="['layout-aside ease-in duration-200 flex h-full', { 'bright': !dark }]">
|
<div :class="['layout-aside ease-in duration-200 flex ', { 'bright': !dark }]">
|
||||||
<div class="flex flex-col h-full border-0 border-r-[1px] border-solid border-[var(--el-color-info-light-8)] box-border bg-[var(--el-color-info-light-8)]">
|
<div class="flex flex-col border-0 border-r-[1px] border-solid border-[var(--el-color-info-light-8)] box-border">
|
||||||
<div class="w-full h-[64px] flex justify-center items-center w-[65px]flex-shrink-0">
|
<!-- <div class="w-full h-[64px] flex justify-center items-center w-[65px]flex-shrink-0">
|
||||||
<div class="w-[40px] h-[40px] rounded-[50%] overflow-hidden">
|
<div class="w-[40px] h-[40px] rounded-[50%] overflow-hidden">
|
||||||
<el-image style="width: 100%; height: 100%" :src="img(logoUrl)" fit="contain">
|
<el-image style="width: 100%; height: 100%" :src="img(logoUrl)" fit="contain">
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="flex justify-center items-center w-full h-[40px]"><img class="max-w-[40px]" src="@/app/assets/images/site_login_logo.png" alt="" object-fit="contain"></div>
|
<div class="flex justify-center items-center w-full h-[40px]"><img class="max-w-[100px]" src="@/app/assets/images/site_login_logo.png" alt="" object-fit="contain"></div>
|
||||||
</template>
|
</template>
|
||||||
</el-image>
|
</el-image>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
<el-scrollbar class="flex-1 w-[65px] one-menu">
|
<el-scrollbar class="w-[65px] one-menu">
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<template v-for="(item, index) in oneMenuData">
|
<template v-for="(item, index) in oneMenuData">
|
||||||
<div v-if="item.meta.show" class="menu-item py-[10px] w-full box-border cursor-pointer" :class="{'is-active':oneMenuActive===item.original_name}" @click="router.push({ name: item.name })">
|
<div v-if="item.meta.show" class="menu-item py-[10px] flex flex-col items-center justify-center w-full box-border cursor-pointer" :class="{'is-active':oneMenuActive===item.original_name}" @click="router.push({ name: item.name })">
|
||||||
<div class="w-[50px] h-full flex items-center justify-center mx-auto">
|
<div class="w-[35px] h-[35px] flex items-center justify-center mx-auto menu-icon" :class="{'is-active':oneMenuActive===item.original_name}">
|
||||||
<template v-if="item.meta.icon">
|
<template v-if="item.meta.icon">
|
||||||
<el-image class="w-[25px] h-[25px] overflow-hidden" :src="item.meta.icon" fit="fill" v-if="isUrl(item.meta.icon)"/>
|
<el-image class="w-[25px] h-[25px] overflow-hidden" :src="item.meta.icon" fit="fill" v-if="isUrl(item.meta.icon)"/>
|
||||||
<icon :name="item.meta.icon" size="25px" v-else />
|
<icon :name="item.meta.icon" size="25px" v-else />
|
||||||
</template>
|
</template>
|
||||||
<icon v-else :name="'iconfont iconshezhi1'" />
|
<icon v-else :name="'iconfont iconshezhi1'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center text-[13px] mt-[5px]">{{ item.meta.short_title || item.meta.title }}</div>
|
<div class="text-center text-[13px] mt-[3px]">{{ item.meta.short_title || item.meta.title }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col two-menu w-[140px] h-[100vh]" v-if="twoMenuData.length">
|
<div class="flex flex-col two-menu w-[185px] " v-if="twoMenuData.length">
|
||||||
<div class="w-[140px] h-[64px] flex items-center justify-center text-[16px]">{{ route.matched[1].meta.title }}</div>
|
<!-- <div class="w-[175px] h-[64px] flex items-center justify-center text-[16px]">{{ route.matched[1].meta.title }}</div> -->
|
||||||
<el-scrollbar class="flex-1">
|
<el-scrollbar class="flex-1">
|
||||||
<el-menu :default-active="route.name" :router="true" class="aside-menu " :collapse="systemStore.menuIsCollapse">
|
<el-menu :default-active="route.name" :default-openeds="defaultOpeneds" :router="true" class="aside-menu" :collapse="systemStore.menuIsCollapse">
|
||||||
<menu-item v-for="(route, index) in twoMenuData" :routes="route" :key="index" />
|
<menu-item v-for="(route, index) in twoMenuData" :routes="route" :key="index" />
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<div class="h-[48px]"></div>
|
<div class="h-[48px]"></div>
|
||||||
@ -62,6 +62,7 @@ const logoUrl = computed(() => {
|
|||||||
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
||||||
})
|
})
|
||||||
const twoMenuData = ref<Record<string, any>[]>([])
|
const twoMenuData = ref<Record<string, any>[]>([])
|
||||||
|
const defaultOpeneds = ref<string[]>([]) // 默认打开的菜单项路径数组
|
||||||
|
|
||||||
const oneMenuData = ref<Record<string, any>[]>([])
|
const oneMenuData = ref<Record<string, any>[]>([])
|
||||||
routers.forEach(item => {
|
routers.forEach(item => {
|
||||||
@ -76,6 +77,7 @@ const oneMenuActive = ref(oneMenuData.value[0].name)
|
|||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
twoMenuData.value = route.matched[1].children ?? []
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
oneMenuActive.value = route.matched[1].name == ADMIN_ROUTE.children[0].name ? route.matched[2].name : route.matched[1].name
|
oneMenuActive.value = route.matched[1].name == ADMIN_ROUTE.children[0].name ? route.matched[2].name : route.matched[1].name
|
||||||
|
defaultOpeneds.value = twoMenuData.value.map(item => item.name)
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -83,12 +85,25 @@ watch(route, () => {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.one-menu{
|
.one-menu{
|
||||||
.menu-item{
|
.menu-item{
|
||||||
|
.menu-icon {
|
||||||
|
background-color: transparent; /* 默认无背景色 */
|
||||||
|
color: #8F9ABF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon.is-active {
|
||||||
|
background-color: var(--el-color-primary); /* 选中时背景色 */
|
||||||
|
color: white; /* 选中时图标颜色变白 */
|
||||||
|
border-radius: 4px; /* 可选:使图标背景为圆形 */
|
||||||
|
}
|
||||||
|
|
||||||
&:hover{
|
&:hover{
|
||||||
color:var(--el-color-primary);
|
color:var(--el-color-primary);
|
||||||
}
|
}
|
||||||
&.is-active{
|
&.is-active{
|
||||||
background-color: var(--el-color-primary) !important;
|
// background-color: var(--el-color-primary) !important;
|
||||||
color: #fff !important;
|
// color: #fff !important;
|
||||||
|
border: none;
|
||||||
|
color:var(--el-color-primary);
|
||||||
}
|
}
|
||||||
span{
|
span{
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -104,12 +119,12 @@ watch(route, () => {
|
|||||||
}
|
}
|
||||||
.two-menu{
|
.two-menu{
|
||||||
.aside-menu:not(.el-menu--collapse) {
|
.aside-menu:not(.el-menu--collapse) {
|
||||||
width: 140px;
|
width: 185px;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding-top: 10px;
|
padding-top: 15px;
|
||||||
.el-menu-item{
|
.el-menu-item{
|
||||||
height: 36px;
|
height: 36px;
|
||||||
margin: 0 8px 4px;
|
margin: 4px 10px;
|
||||||
padding: 0 8px !important;
|
padding: 0 8px !important;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
span{
|
span{
|
||||||
@ -117,17 +132,21 @@ watch(route, () => {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
&.is-active{
|
&.is-active{
|
||||||
background-color: var(--el-color-info-light-8) !important;
|
// background-color: var(--el-color-info-light-8) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
}
|
}
|
||||||
&:hover{
|
&:hover{
|
||||||
background-color: var(--el-color-info-light-8) !important;
|
// background-color: var(--el-color-info-light-8) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.el-sub-menu{
|
.el-sub-menu{
|
||||||
margin-bottom: 8px;
|
width: 185px;
|
||||||
|
margin: 4px 0px;
|
||||||
|
// margin-bottom: 8px;
|
||||||
.el-sub-menu__title{
|
.el-sub-menu__title{
|
||||||
margin: 0 8px 4px;
|
margin: 0px 10px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@ -138,7 +157,8 @@ watch(route, () => {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
&:hover{
|
&:hover{
|
||||||
background-color: var(--el-color-info-light-8) !important;
|
// background-color: var(--el-color-info-light-8) !important;
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
.el-icon.el-sub-menu__icon-arrow{
|
.el-icon.el-sub-menu__icon-arrow{
|
||||||
@ -146,7 +166,7 @@ watch(route, () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.el-menu-item{
|
.el-menu-item{
|
||||||
padding-left: 20px !important;
|
padding-left: 25px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,16 +195,16 @@ watch(route, () => {
|
|||||||
.layout-aside .el-scrollbar__wrap--hidden-default, .layout-aside .el-scrollbar{
|
.layout-aside .el-scrollbar__wrap--hidden-default, .layout-aside .el-scrollbar{
|
||||||
overflow: inherit !important;
|
overflow: inherit !important;
|
||||||
}
|
}
|
||||||
.layout-aside .menu-item.is-active{
|
// .layout-aside .menu-item.is-active{
|
||||||
position: relative;
|
// position: relative;
|
||||||
&:after{
|
// &:after{
|
||||||
content: "";
|
// content: "";
|
||||||
position: absolute;
|
// position: absolute;
|
||||||
top: 0;
|
// top: 0;
|
||||||
bottom: 0;
|
// bottom: 0;
|
||||||
width: 1px;
|
// width: 1px;
|
||||||
background: var(--el-color-primary);
|
// background: var(--el-color-primary);
|
||||||
right: -1px;
|
// right: -1px;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<template v-if="meta.show">
|
<template v-if="meta.show">
|
||||||
<el-sub-menu v-if="routes.children" :index="String(routes.name)">
|
<el-sub-menu v-if="routes.children" :index="String(routes.name)">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div v-if="meta.icon && routes.meta.class == 1" class="w-[16px] h-[16px] relative flex items-center">
|
<div v-if="meta.icon " class="w-[13px] h-[13px] mr-[10rpx] relative flex justify-center items-center">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
</div>
|
</div>
|
||||||
<span :class="['ml-[10px]', {'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}]">{{ meta.title }}</span>
|
<span :class="['ml-[10px]', {'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}]">{{ meta.title }}</span>
|
||||||
@ -11,6 +11,9 @@
|
|||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
<el-menu-item v-else :index="String(routes.name)" :route="routes.path">
|
<el-menu-item v-else :index="String(routes.name)" :route="routes.path">
|
||||||
<template #title>
|
<template #title>
|
||||||
|
<div v-if="meta.icon " class="w-[13px] h-[13px] mr-[10rpx] relative flex justify-center items-center">
|
||||||
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
|
</div>
|
||||||
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
@ -33,6 +36,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const meta = computed(() => props.routes.meta)
|
const meta = computed(() => props.routes.meta)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||