mirror of
https://gitee.com/niucloud-team/niucloud-admin.git
synced 2026-01-29 14:28:10 +00:00
update admin
This commit is contained in:
parent
e1d72b448d
commit
21d960df44
@ -12,7 +12,6 @@ import useSystemStore from '@/stores/modules/system'
|
||||
import useAppStore from '@/stores/modules/app'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
import { setThemeColor } from '@/utils/common'
|
||||
import { language } from '@/lang'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
@ -20,15 +19,14 @@ const route = useRoute()
|
||||
// 初始化设置语言
|
||||
const systemStore = useSystemStore()
|
||||
const locale = computed(() => (systemStore.lang === 'zh-cn' ? zhCn : en))
|
||||
language.loadLocaleMessages(route.path, systemStore.lang)
|
||||
|
||||
const toggleDark = useToggle(useDark())
|
||||
|
||||
watch(route, () => {
|
||||
useAppStore().$patch(state => {
|
||||
state.route = route.path
|
||||
state.route = route.meta.view || route.path
|
||||
})
|
||||
})
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
// 设置主题色
|
||||
|
||||
@ -2,13 +2,22 @@ import request from '@/utils/request'
|
||||
|
||||
/***************************************************** 自定义页面 ****************************************************/
|
||||
|
||||
/**
|
||||
* 获取自定义页面分页列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getDiyPageList(params: Record<string, any>) {
|
||||
return request.get(`diy/diy`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义页面列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getDiyPageList(params: Record<string, any>) {
|
||||
return request.get(`diy/diy`, { params })
|
||||
export function getDiyList(params: Record<string, any>) {
|
||||
return request.get(`diy/list`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,7 +35,7 @@ export function getDiyPageInfo(id: number) {
|
||||
* @returns
|
||||
*/
|
||||
export function addDiyPage(params: Record<string, any>) {
|
||||
return request.post('diy/diy', params, { showSuccessMessage: true })
|
||||
return request.post('diy/diy', params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,7 +43,7 @@ export function addDiyPage(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function editDiyPage(params: Record<string, any>) {
|
||||
return request.put(`diy/diy/${params.id}`, params, { showSuccessMessage: true })
|
||||
return request.put(`diy/diy/${params.id}`, params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,7 +51,7 @@ export function editDiyPage(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function setUseDiyPage(params: Record<string, any>) {
|
||||
return request.put(`diy/use`, params, { showSuccessMessage: true })
|
||||
return request.put(`diy/use`, params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,7 +59,7 @@ export function setUseDiyPage(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function editDiyPageShare(params: Record<string, any>) {
|
||||
return request.put(`diy/diy/share`, params, { showSuccessMessage: true })
|
||||
return request.put(`diy/diy/share`, params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,28 +68,28 @@ export function editDiyPageShare(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function deleteDiyPage(id: number) {
|
||||
return request.delete(`diy/diy/${id}`, { showSuccessMessage: true })
|
||||
return request.delete(`diy/diy/${id}`, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义页面初始化数据
|
||||
*/
|
||||
export function initPage(params: Record<string, any>) {
|
||||
return request.get(`diy/init`, { params })
|
||||
return request.get(`diy/init`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义链接列表
|
||||
*/
|
||||
export function getLink(params: Record<string, any>) {
|
||||
return request.get(`diy/link`, { params })
|
||||
return request.get(`diy/link`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底部导航数据
|
||||
*/
|
||||
export function getDiyBottom(params: Record<string, any>) {
|
||||
return request.get(`diy/bottom`, { params })
|
||||
return request.get(`diy/bottom`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,14 +98,14 @@ export function getDiyBottom(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function setDiyBottom(params: Record<string, any>) {
|
||||
return request.post('diy/bottom', params, { showSuccessMessage: true })
|
||||
return request.post('diy/bottom', params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页面模板
|
||||
*/
|
||||
export function getDiyTemplate(params: Record<string, any>) {
|
||||
return request.get(`diy/template`, { params })
|
||||
return request.get(`diy/template`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +114,7 @@ export function getDiyTemplate(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getDiyRouteList(params: Record<string, any>) {
|
||||
return request.get(`diy/route`, { params })
|
||||
return request.get(`diy/route`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,7 +122,7 @@ export function getDiyRouteList(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function getDiyRouteInfo(params: Record<string, any>) {
|
||||
return request.get(`diy/route/info`, { params });
|
||||
return request.get(`diy/route/info`, {params});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,5 +130,32 @@ export function getDiyRouteInfo(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function editDiyRouteShare(params: Record<string, any>) {
|
||||
return request.put(`diy/route/share`, params, { showSuccessMessage: true })
|
||||
return request.put(`diy/route/share`, params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义页面列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getDecoratePage(params: Record<string, any>) {
|
||||
return request.get(`diy/decorate`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换模板
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function changeTemplate(params: Record<string, any>) {
|
||||
return request.put(`diy/change`, params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预览数据
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getPreviewData(params: Record<string, any>) {
|
||||
return request.put(`diy/preview`, params, {showSuccessMessage: false})
|
||||
}
|
||||
@ -216,4 +216,39 @@ export function getLogList(params: Record<string, any>) {
|
||||
*/
|
||||
export function getLogInfo(id: number) {
|
||||
return request.get(`site/log/${id}`)
|
||||
}
|
||||
/***************************************************** 账单列表 **************************************************/
|
||||
|
||||
/**
|
||||
* 获取账单列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getAccountList(params: Record<string, any>) {
|
||||
return request.get(`site/account`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账单详情
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getAccountInfo(id: number) {
|
||||
return request.get(`site/account/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账单统计
|
||||
* @returns
|
||||
*/
|
||||
export function getAccountStat() {
|
||||
return request.get(`site/account/stat`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账单类型
|
||||
* @returns
|
||||
*/
|
||||
export function getAccountType() {
|
||||
return request.get(`site/account/type`)
|
||||
}
|
||||
@ -244,6 +244,26 @@ export function moveAttachment(params: Record<string, any>) {
|
||||
return request.put(`sys/attachment/batchmove`, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取menu菜单
|
||||
*/
|
||||
export function getAuthMenu() {
|
||||
return request.get(`auth/site/showmenu`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取快捷菜单
|
||||
*/
|
||||
export function getShortcutMenu() {
|
||||
return request.get(`sys/config/shortcut_menu`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加快捷菜单
|
||||
*/
|
||||
export function setShortcutMenu(params: Record<string, any>) {
|
||||
return request.put(`sys/config/shortcut_menu`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/***************************************************** 地址管理 ****************************************************/
|
||||
|
||||
@ -265,6 +285,21 @@ export function getAreatree(level: number = 1) {
|
||||
return request.get(`sys/area/tree/${level}`)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取地址信息
|
||||
*/
|
||||
export function getAddressInfo(params: any) {
|
||||
return request.get(`sys/area/get_info`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地址信息
|
||||
*/
|
||||
export function getContraryAddress(params: any) {
|
||||
return request.get(`sys/area/contrary`, { params })
|
||||
}
|
||||
|
||||
/***************************************************** 存储设置 ****************************************************/
|
||||
|
||||
/**
|
||||
@ -345,7 +380,7 @@ export function setTransferInfo(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getCronList(params: any) {
|
||||
return request.get(`sys/cron`, { params })
|
||||
return request.get(`sys/schedule/list`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -481,5 +516,17 @@ export function getMap() {
|
||||
return request.get(`sys/config/map`)
|
||||
}
|
||||
|
||||
/***************************************************** 首页 ****************************************************/
|
||||
/**
|
||||
* 获取首页列表
|
||||
*/
|
||||
export function getIndexList() {
|
||||
return request.get(`sys/config/site_index`)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置首页模版
|
||||
*/
|
||||
export function setIndexList(params: Record<string, any>) {
|
||||
return request.put(`sys/config/site_index`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
BIN
admin/src/assets/images/index/add_menu.png
Normal file
BIN
admin/src/assets/images/index/add_menu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 897 B |
BIN
admin/src/assets/images/index/del_model.png
Normal file
BIN
admin/src/assets/images/index/del_model.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 773 B |
BIN
admin/src/assets/images/index/edit.png
Normal file
BIN
admin/src/assets/images/index/edit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 550 B |
BIN
admin/src/assets/images/index/model_tag.png
Normal file
BIN
admin/src/assets/images/index/model_tag.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 157 B |
BIN
admin/src/assets/images/index/site_img.png
Normal file
BIN
admin/src/assets/images/index/site_img.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
admin/src/assets/images/iphone_bg.png
Normal file
BIN
admin/src/assets/images/iphone_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
@ -44,7 +44,6 @@ class Language {
|
||||
this.setI18nLanguage(locale)
|
||||
return nextTick()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.setI18nLanguage(locale)
|
||||
return nextTick()
|
||||
}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
{
|
||||
"menuName": "菜单名称",
|
||||
"menuType": "类型",
|
||||
"authId": "权限标识",
|
||||
"menuTypeDir": "目录",
|
||||
"menuTypeMenu": "菜单",
|
||||
"menuTypeButton": "按钮",
|
||||
"menuDeleteTips": "确定要删除该菜单吗?",
|
||||
"addMenu": "添加菜单",
|
||||
"updateMenu": "编辑菜单",
|
||||
"routePath": "路由路径",
|
||||
"viewPath": "组件路径",
|
||||
"parentMenu": "父级菜单",
|
||||
"menuIcon": "菜单图标",
|
||||
"sort":"权重",
|
||||
"menuKey":"菜单标识",
|
||||
"menuNamePlaceholder": "请输入菜单名称",
|
||||
"menuKeyPlaceholder": "请输入菜单标识",
|
||||
"menuKeyValidata":"菜单标识只能使用字母数字下划线并且开头不能为数字",
|
||||
"routePathPlaceholder": "请输入路由路径",
|
||||
"viewPathPlaceholder": "请输入组件路径",
|
||||
"authIdPlaceholder": "请输入权限标识",
|
||||
"selectIconPlaceholder": "请选择菜单图标",
|
||||
"topLevel": "顶级"
|
||||
}
|
||||
@ -90,7 +90,9 @@
|
||||
"503": "服务不可用",
|
||||
"504": "网络超时",
|
||||
"505": "http版本不支持该请求",
|
||||
"timeout": "网络请求超时!"
|
||||
"timeout": "网络请求超时!",
|
||||
"requestError": "请求错误",
|
||||
"errNetwork": "网络请求错误"
|
||||
},
|
||||
"linkPlaceholder": "请选择跳转链接",
|
||||
"selectLinkTips": "链接选择",
|
||||
@ -105,5 +107,8 @@
|
||||
"emptyApp": "暂未安装任何应用",
|
||||
"newInfo": "最新消息",
|
||||
"mapSetting": "地图设置",
|
||||
"mapKey": "腾讯地图KEY"
|
||||
"mapKey": "腾讯地图KEY",
|
||||
"indexTemplate": "首页模版",
|
||||
"indexSwitch": "切换首页",
|
||||
"indexWarning": "你确定要切换首页吗?"
|
||||
}
|
||||
12
admin/src/lang/zh-cn/decorate.preview.json
Normal file
12
admin/src/lang/zh-cn/decorate.preview.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"preview": "预览",
|
||||
"weapp": "微信小程序",
|
||||
"wechat": "微信公众号",
|
||||
"link": "链接",
|
||||
"copy": "复制",
|
||||
"copySuccess": "复制成功",
|
||||
"weappNotSet": "小程序未配置",
|
||||
"developTitle": "开发环境配置",
|
||||
"wapDomain": "wap域名(WAP_DOMAIN)",
|
||||
"wapDomainPlaceholder": "请输入wap域名"
|
||||
}
|
||||
@ -1,9 +1,26 @@
|
||||
{
|
||||
"developTitle": "开发环境配置",
|
||||
"wapDomain": "wap域名(WAP_DOMAIN)",
|
||||
"wapDomainPlaceholder": "请输入wap域名",
|
||||
"pageSet": "页面设置",
|
||||
"tabEditContent": "内容",
|
||||
"tabEditStyle": "样式",
|
||||
"pageStyle": "页面样式",
|
||||
"pageContent": "页面内容",
|
||||
"pageName": "页面名称",
|
||||
"pageNamePlaceholder": "请输入页面名称",
|
||||
"pageBgColor": "页面颜色",
|
||||
"bgUrl": "背景图片",
|
||||
"marginSet": "边距设置",
|
||||
"componentStyleTitle": "组件样式",
|
||||
"bottomBgColor": "底部背景",
|
||||
"bottomBgTips": "底部背景包含边距和圆角",
|
||||
"componentBgColor": "组件背景",
|
||||
"marginTop": "上边距",
|
||||
"marginBottom": "下边距",
|
||||
"marginBoth": "左右边距",
|
||||
"topRounded": "上圆角",
|
||||
"bottomRounded": "下圆角",
|
||||
"warmPrompt": "温馨提示",
|
||||
"leavePageTitleTips": "确定离开此页面?",
|
||||
"leavePageContentTips": "系统可能不会保存您所做的更改。",
|
||||
@ -27,7 +44,12 @@
|
||||
"imageAdsTips": "建议上传尺寸相同的图片,推荐尺寸750*350",
|
||||
"addImageAd": "添加图片",
|
||||
"imageUrlTip": "请上传图片",
|
||||
"imageHeight": "图片高度",
|
||||
"imageHeightPlaceholder": "请输入图片高度",
|
||||
"imageHeightRegNum": "图片高度格式错误,请输入数字",
|
||||
"articleData": "文章数据",
|
||||
"articleStyle": "文章样式",
|
||||
"articleBgColor": "文章背景",
|
||||
"dataSources": "数据来源",
|
||||
"defaultSources": "默认",
|
||||
"manualSelectionSources": "手动选择",
|
||||
@ -65,6 +87,7 @@
|
||||
"addGraphicNav": "添加导航",
|
||||
"blankHeightSet": "高度设置",
|
||||
"blankHeight": "空白高度",
|
||||
"styleSet": "风格设置",
|
||||
"titleStyle": "标题样式",
|
||||
"selectStyle": "风格选择",
|
||||
"titleContent": "标题内容",
|
||||
@ -73,6 +96,7 @@
|
||||
"textAlign": "对齐方式",
|
||||
"textAlignLeft": "居左",
|
||||
"textAlignCenter": "居中",
|
||||
"textSet": "文字设置",
|
||||
"textFontSize": "文字大小",
|
||||
"textFontWeight": "文字粗细",
|
||||
"fontWeightBold": "加粗",
|
||||
@ -84,5 +108,6 @@
|
||||
"moreContent": "“更多”按钮内容",
|
||||
"more": "文字",
|
||||
"morePlaceholder": "请输入文字",
|
||||
"moreIsShow": "是否显示"
|
||||
"moreIsShow": "是否显示",
|
||||
"memberStyle": "会员样式"
|
||||
}
|
||||
@ -1,12 +1,19 @@
|
||||
{
|
||||
"decorate": "装修",
|
||||
"pageDecorate": "页面装修",
|
||||
"changeTemplate": "切换模板",
|
||||
"templateName": "模板名称",
|
||||
"preview": "预览",
|
||||
"weapp": "微信小程序",
|
||||
"wechat": "微信公众号",
|
||||
"link": "链接",
|
||||
"copy": "复制",
|
||||
"copySuccess": "复制成功",
|
||||
"weappNotSet": "小程序未配置",
|
||||
"hopeBeforeTip": "我希望把",
|
||||
"hopeAfterTip": "切换成其他样式",
|
||||
"changeTemplateTip": "选择",
|
||||
"template": "模板",
|
||||
"changeMyPageTip": "选择微页面作为",
|
||||
"createPage": "创建微页面",
|
||||
"myPage": "我的微页面",
|
||||
"refreshPage": "刷新",
|
||||
"placeholderTemplate": "请选择一个模板",
|
||||
"placeholderMyPage": "请选择一个微页面",
|
||||
"developTitle": "开发环境配置",
|
||||
"wapDomain": "wap域名(WAP_DOMAIN)",
|
||||
"wapDomainPlaceholder": "请输入wap域名"
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
{
|
||||
"title": "页面名称",
|
||||
"typeName": "页面模板",
|
||||
"addType": "页面类型",
|
||||
"addPageTips": "创建新页面",
|
||||
"pageTemplatePlaceholder": "请选择页面模板",
|
||||
"pageTypePlaceholder": "请选择页面模板",
|
||||
"nameMax": "名称不能超过12个字符",
|
||||
"templateName": "模板名称",
|
||||
"empty": "空白",
|
||||
"templateName": "已有模板",
|
||||
"emptyTemplate": "空模板",
|
||||
"status": "状态",
|
||||
"updateTime": "更新时间",
|
||||
"use": "使用",
|
||||
|
||||
12
admin/src/lang/zh-cn/diy.preview.json
Normal file
12
admin/src/lang/zh-cn/diy.preview.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"preview": "预览",
|
||||
"weapp": "微信小程序",
|
||||
"wechat": "微信公众号",
|
||||
"link": "链接",
|
||||
"copy": "复制",
|
||||
"copySuccess": "复制成功",
|
||||
"weappNotSet": "小程序未配置",
|
||||
"developTitle": "开发环境配置",
|
||||
"wapDomain": "wap域名(WAP_DOMAIN)",
|
||||
"wapDomainPlaceholder": "请输入wap域名"
|
||||
}
|
||||
36
admin/src/lang/zh-cn/diy.route.json
Normal file
36
admin/src/lang/zh-cn/diy.route.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"title": "页面名称",
|
||||
"typeName": "页面模板",
|
||||
"addPageTips": "创建新页面",
|
||||
"pageTemplatePlaceholder": "请选择页面模板",
|
||||
"nameMax": "名称不能超过12个字符",
|
||||
"templateName": "模板名称",
|
||||
"empty": "空白",
|
||||
"status": "状态",
|
||||
"updateTime": "更新时间",
|
||||
"use": "使用",
|
||||
"isUse": "使用中",
|
||||
"unused": "未使用",
|
||||
"all": "全部",
|
||||
"basicRoute": "基础页面",
|
||||
"diyPage": "自定义页面",
|
||||
"wapUrl": "wap链接",
|
||||
"weappUrl": "小程序链接",
|
||||
"shareLink": "分享链接",
|
||||
"copy": "复制",
|
||||
"copySuccess": "复制成功",
|
||||
"titlePlaceholder": "请输入页面名称",
|
||||
"addDiyPage": "添加页面",
|
||||
"diyPageDeleteTips": "确定要删除该自定义页面吗?",
|
||||
"promote": "推广",
|
||||
"share": "分享",
|
||||
"shareSet": "分享设置",
|
||||
"sharePage": "分享页面",
|
||||
"wechat": "微信公众号",
|
||||
"weapp": "微信小程序",
|
||||
"shareTitle": "分享标题",
|
||||
"shareTitlePlaceholder": "请输入分享标题",
|
||||
"shareDesc": "分享描述",
|
||||
"shareDescPlaceholder": "请输入分享描述",
|
||||
"shareImageUrl": "分享图片"
|
||||
}
|
||||
33
admin/src/lang/zh-cn/finance.account.json
Normal file
33
admin/src/lang/zh-cn/finance.account.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"id":"主键",
|
||||
"accountDetail":"账单详情",
|
||||
"detail":"详情",
|
||||
"idPlaceholder":"请输入主键",
|
||||
"siteId":"站点id",
|
||||
"siteIdPlaceholder":"请输入站点id",
|
||||
"type":"账单类型",
|
||||
"money":"账单金额",
|
||||
"moneyPlaceholder":"请输入账单金额",
|
||||
"tradeNo":"账单编号",
|
||||
"tradeNoPlaceholder":"请输入账单编号",
|
||||
"createTime":"添加时间",
|
||||
"createTimePlaceholder":"请输入添加时间",
|
||||
"addSiteAccountLog":"添加站点账单记录",
|
||||
"updateSiteAccountLog":"编辑站点账单记录",
|
||||
"siteAccountLogDeleteTips":"确定要删除该站点账单记录吗?",
|
||||
"totalPay":"累计收款(元)",
|
||||
"totalRefund":"累计退款(元)",
|
||||
"totalTransfer":"累计转账(元)",
|
||||
"accountType": "请选择退款类型",
|
||||
"startDate": "开始时间",
|
||||
"endDate": "结束时间",
|
||||
"transferNo" : "转账单号",
|
||||
"transferTime": "转账时间",
|
||||
"transferType": "转账类型",
|
||||
"transferMoney": "转账金额",
|
||||
"transferRemark": "转账说明",
|
||||
"outTradeNo": "单号",
|
||||
"refundMoney": "退款金额",
|
||||
"failReason": "退款说明",
|
||||
"body": "说明"
|
||||
}
|
||||
@ -40,5 +40,28 @@
|
||||
"accumulative":"累计",
|
||||
"officialAccount": "Niucloud官方公众号",
|
||||
"officialAccountDesc": "微信扫码关注",
|
||||
"WeCom": "添加企业微信群"
|
||||
"WeCom": "添加企业微信群",
|
||||
"path": "地址",
|
||||
"menuName": "名称",
|
||||
"menuNamePlaceholder": "模版名称",
|
||||
"menuBgColor": "背景颜色",
|
||||
"menuImg": "选择图标",
|
||||
"menuDesc": "描述",
|
||||
"addShortcutMenu": "添加快捷模版",
|
||||
"appTemplate": "应用模块",
|
||||
"siteType": "站点类型",
|
||||
"periodTime": "有效期",
|
||||
"renew": "续费",
|
||||
"selectModel": "选择模版",
|
||||
"addMenu": "添加模块",
|
||||
"shortcutLink": "链接",
|
||||
"emptyMenu": "暂无快捷模块",
|
||||
"select": "选择",
|
||||
"custom": "自定义",
|
||||
"accessSite": "访问站点",
|
||||
"pathSelect": "选择链接",
|
||||
"bgColorPlaceholder": "请选择背景色",
|
||||
"iconPlaceholder": "请选择图标",
|
||||
"pathPlaceholder": "请选择链接",
|
||||
"descPlaceholder": "输入描述语…"
|
||||
}
|
||||
@ -17,6 +17,5 @@
|
||||
"weappTempKey" : "模板编号",
|
||||
"smsId":"短信模版ID",
|
||||
"smsIdPlaceholder":"短信模版ID",
|
||||
"noticeType":"消息类型"
|
||||
|
||||
"noticeType":"消息类型"
|
||||
}
|
||||
@ -34,6 +34,8 @@
|
||||
"columnComment": "字段描述",
|
||||
"columnType": "类型",
|
||||
"fieldAttribute": "字段属性",
|
||||
"addAndEdit":"添加编辑",
|
||||
"listSearch":"列表查询",
|
||||
"isPk":"是否主键",
|
||||
"isRequired":"是否必填",
|
||||
"isInsert":"是否添加",
|
||||
@ -41,7 +43,7 @@
|
||||
"isLists":"列表展示",
|
||||
"isSearch":"是否搜索",
|
||||
"isQuery":"是否查询",
|
||||
"queryType":"查询方式",
|
||||
"queryType":"搜索方式",
|
||||
"viewType":"显示方式",
|
||||
"pkRepeatTip": "只能添加一个主键",
|
||||
"formInput":"文本框",
|
||||
|
||||
@ -13,5 +13,8 @@
|
||||
"nextTime":"下次执行时间",
|
||||
"task":"任务命令",
|
||||
"data":"附加参数",
|
||||
"statusDesc":"最后执行结果"
|
||||
"statusDesc":"最后执行结果",
|
||||
"key": "key",
|
||||
"timeClass": "时间类型",
|
||||
"executeTime": "执行时间"
|
||||
}
|
||||
@ -14,7 +14,12 @@
|
||||
<icon :name="meta.icon" class="absolute top-[50%] -translate-y-[50%]" />
|
||||
</div>
|
||||
<template #title>
|
||||
<span :class="['ml-[10px]', {'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}]">{{ meta.title }}</span>
|
||||
<div class="relative">
|
||||
<span :class="['ml-[10px]', {'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}]">{{ meta.title }}</span>
|
||||
<div class="absolute top-[50%] -translate-y-[50%] right-[-288%]" @click="checkIndexList">
|
||||
<img v-if="routes.path == '/site/siteindex'" class="w-[12px] h-[12px]" src="@/assets/images/index/model_tag.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item v-else :index="String(routes.name)" :route="routePath">
|
||||
@ -23,13 +28,35 @@
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
<el-dialog v-model="showDialog" :title="t('indexTemplate')" width="550px" :destroy-on-close="true" >
|
||||
<div class="flex flex-wrap">
|
||||
<div v-for="(items, index) in indexList" :key="index" v-if="index_path == ''">
|
||||
<div @click="index_path = items.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="items.is_use == 1 ? 'selected' : '' ">
|
||||
<span >{{ items.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(itemTo, indexTo) in indexList" :key="indexTo" v-else>
|
||||
<div @click="index_path = itemTo.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="index_path == itemTo.view_path ? 'selected' : '' ">
|
||||
<span >{{ itemTo.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="submitIndex">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from '@/lang'
|
||||
import { getIndexList, setIndexList } from '@/api/sys'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { CollectionTag } from '@element-plus/icons-vue'
|
||||
import { computed } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import menuItem from './menu-item.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const props = defineProps({
|
||||
routes: {
|
||||
type: Object,
|
||||
@ -45,6 +72,26 @@ const meta = computed(() => props.routes.meta)
|
||||
const resolvePath = (path: string) => {
|
||||
return `${props.routePath}/${path}`
|
||||
}
|
||||
|
||||
const indexList = ref();
|
||||
const showDialog = ref(false)
|
||||
const checkIndexList = () => {
|
||||
getIndexList().then(res => {
|
||||
showDialog.value = true
|
||||
indexList.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const index_path = ref('');
|
||||
const submitIndex = () => {
|
||||
setIndexList({
|
||||
view_path: index_path.value
|
||||
}).then(() => {
|
||||
showDialog.value = false
|
||||
router.go(0)
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -59,4 +106,8 @@ const resolvePath = (path: string) => {
|
||||
.el-alert .el-alert__description{
|
||||
margin-top: 0;
|
||||
}
|
||||
.selected {
|
||||
color: #fff;
|
||||
background-color: #2C3EEF;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -23,7 +23,12 @@
|
||||
<el-col :span="12">
|
||||
<div class="right-panel h-full flex items-center justify-end">
|
||||
<!-- 预览 只有站点时展示-->
|
||||
|
||||
<i class="iconfont iconlingdang-xianxing cursor-pointer px-[8px]" :title="t('newInfo')" v-if="appType == 'site'"></i>
|
||||
<!-- 切换首页 -->
|
||||
<div class="navbar-item flex items-center h-full cursor-pointer" v-if="appType == 'site'" @click="checkIndexList">
|
||||
<icon name="iconfont-iconqiehuan" :title="t('indexSwitch')"/>
|
||||
</div>
|
||||
<!-- 切换语言 -->
|
||||
<div class="navbar-item flex items-center h-full cursor-pointer">
|
||||
<switch-lang />
|
||||
@ -55,6 +60,26 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="showDialog" :title="t('indexTemplate')" width="550px" :destroy-on-close="true" >
|
||||
<div class="flex flex-wrap">
|
||||
<div v-for="(items, index) in indexList" :key="index" v-if="index_path == ''">
|
||||
<div @click="index_path = items.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="items.is_use == 1 ? 'selected' : '' ">
|
||||
<span >{{ items.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(itemTo, indexTo) in indexList" :key="indexTo" v-else>
|
||||
<div @click="index_path = itemTo.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="index_path == itemTo.view_path ? 'selected' : '' ">
|
||||
<span >{{ itemTo.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="submitIndex">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
@ -70,6 +95,9 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { t } from '@/lang'
|
||||
import storage from '@/utils/storage'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import { getIndexList, setIndexList } from '@/api/sys'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const appType = storage.get('app_type')
|
||||
const { toggle: toggleFullscreen, isFullscreen } = useFullscreen()
|
||||
@ -138,7 +166,7 @@ const toggleMenuCollapse = () => {
|
||||
|
||||
// 刷新路由
|
||||
const refreshRouter = () => {
|
||||
if (!appStore.routeRefrehTag) return
|
||||
if (!appStore.routeRefreshTag) return
|
||||
appStore.refreshRouterView()
|
||||
}
|
||||
|
||||
@ -153,6 +181,25 @@ const breadcrumb = computed(() => {
|
||||
const backFn = () => {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
const indexList = ref();
|
||||
const showDialog = ref(false)
|
||||
const checkIndexList = () => {
|
||||
getIndexList().then(res => {
|
||||
showDialog.value = true
|
||||
indexList.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const index_path = ref('');
|
||||
const submitIndex = () => {
|
||||
setIndexList({
|
||||
view_path: index_path.value
|
||||
}).then(() => {
|
||||
showDialog.value = false
|
||||
router.go(0)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -167,4 +214,16 @@ const backFn = () => {
|
||||
&:hover {
|
||||
background-color: var(--el-bg-color-page);
|
||||
}
|
||||
}</style>
|
||||
}
|
||||
.index-item {
|
||||
background-color: var(--el-bg-color-page);
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: #2C3EEF;
|
||||
}
|
||||
}
|
||||
.selected {
|
||||
color: #fff;
|
||||
background-color: #2C3EEF;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<el-main :class="['main-wrap h-full p-0',{'bg-page': dark}]">
|
||||
<el-scrollbar>
|
||||
<div class="p-[10px]">
|
||||
<router-view v-slot="{ Component, route }" v-if="appStore.routeRefrehTag">
|
||||
<router-view v-slot="{ Component, route }" v-if="appStore.routeRefreshTag">
|
||||
<keep-alive :include="tabbarStore.tabNames">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createRouter, createWebHistory, RouteLocationRaw } from 'vue-router'
|
||||
import { createRouter, createWebHistory, RouteLocationRaw, RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import NProgress from 'nprogress'
|
||||
import 'nprogress/nprogress.css'
|
||||
import { STATIC_ROUTES, NO_LOGIN_ROUTES, ROOT_ROUTER, ADMIN_ROUTE, SITE_ROUTE, DECORATE_ROUTER, findFirstValidRoute } from './routers'
|
||||
@ -24,6 +24,17 @@ router.push = (to: RouteLocationRaw) => {
|
||||
return originPush(route)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写resolve方法
|
||||
*/
|
||||
const originResolve = router.resolve
|
||||
router.resolve = (to: RouteLocationRaw, currentLocation?: RouteLocationNormalizedLoaded) => {
|
||||
const route = typeof to == 'string' ? urlToRouteRaw(to) : to
|
||||
const paths = route.path.split('/').filter((item: string) => { return item })
|
||||
route.path = ['admin', 'site', 'decorate'].indexOf(paths[0]) == -1 ? `/${getAppType()}${route.path}` : route.path
|
||||
return originResolve(route, currentLocation)
|
||||
}
|
||||
|
||||
// 全局前置守卫
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.configure({ showSpinner: false })
|
||||
@ -39,7 +50,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
setWindowTitle(title)
|
||||
|
||||
// 加载语言包
|
||||
await language.loadLocaleMessages(to.path, useSystemStore().lang);
|
||||
await language.loadLocaleMessages((to.meta.view || to.path), useSystemStore().lang);
|
||||
|
||||
let matched: any = to.matched;
|
||||
|
||||
|
||||
@ -122,7 +122,8 @@ const createRoute = function (route: Route, parentRoute: RouteRecordRaw | null =
|
||||
icon: route.icon,
|
||||
type: route.menu_type,
|
||||
show: route.is_show,
|
||||
app: route.app_type
|
||||
app: route.app_type,
|
||||
view: route.view_path
|
||||
}
|
||||
}
|
||||
if (route.menu_type == 0) {
|
||||
|
||||
@ -4,7 +4,7 @@ import NProgress from 'nprogress'
|
||||
|
||||
interface App {
|
||||
route: string,
|
||||
routeRefrehTag: boolean,
|
||||
routeRefreshTag: boolean,
|
||||
pageReturn: boolean
|
||||
}
|
||||
|
||||
@ -12,17 +12,17 @@ const useAppStore = defineStore('app', {
|
||||
state: (): App => {
|
||||
return {
|
||||
route: '',
|
||||
routeRefrehTag: true,
|
||||
routeRefreshTag: true,
|
||||
pageReturn: false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
refreshRouterView() {
|
||||
this.routeRefrehTag = false
|
||||
this.routeRefreshTag = false
|
||||
NProgress.start()
|
||||
|
||||
nextTick(() => {
|
||||
this.routeRefrehTag = true
|
||||
this.routeRefreshTag = true
|
||||
NProgress.done()
|
||||
})
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ const useDiyStore = defineStore('diy', {
|
||||
name: '', // 页面标识
|
||||
type: '', // 页面模板
|
||||
typeName: '', // 页面模板名称
|
||||
templateName : '', // 页面模板标识
|
||||
isDefault: 0, // 是否默认页面
|
||||
predefineColors: [
|
||||
'#F4391c',
|
||||
|
||||
@ -33,9 +33,9 @@ const useSystemStore = defineStore('user', {
|
||||
this.userInfo = res.data.userinfo
|
||||
setToken(res.data.token)
|
||||
storage.set({ key: 'userinfo', data: res.data.userinfo })
|
||||
storage.set({ key: 'siteId', data: res.data.site_id })
|
||||
storage.set({ key: 'siteId', data: res.data.site_info.site_code })
|
||||
storage.set({ key: 'siteInfo', data: res.data.site_info })
|
||||
storage.set({ key: 'comparisonSiteIdStorage', data: res.data.site_id })
|
||||
storage.set({ key: 'comparisonSiteIdStorage', data: res.data.site_info.site_code })
|
||||
storage.set({ key: 'comparisonTokenStorage', data: res.data.token })
|
||||
resolve(res)
|
||||
})
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3883393 */
|
||||
src: url('//at.alicdn.com/t/c/font_3883393_pwtqr51nps.woff2?t=1686032889350') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_pwtqr51nps.woff?t=1686032889350') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_pwtqr51nps.ttf?t=1686032889350') format('truetype');
|
||||
src: url('//at.alicdn.com/t/c/font_3883393_pgas1v4bw2a.woff2?t=1686965237342') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_pgas1v4bw2a.woff?t=1686965237342') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_3883393_pgas1v4bw2a.ttf?t=1686965237342') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconqiehuan:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.iconxiangyoujiantou:before {
|
||||
content: "\e660";
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ class Request {
|
||||
|
||||
constructor() {
|
||||
this.instance = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_URL,
|
||||
baseURL: import.meta.env.VITE_APP_BASE_URL.substr(-1) == '/' ? import.meta.env.VITE_APP_BASE_URL : `${import.meta.env.VITE_APP_BASE_URL}/`,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded;',
|
||||
@ -111,7 +111,7 @@ class Request {
|
||||
* @param err
|
||||
*/
|
||||
private handleNetworkError(err: any) {
|
||||
let errMessage = t('axios.requestError')
|
||||
let errMessage = ''
|
||||
|
||||
if (err.response && err.response.status) {
|
||||
const errStatus = err.response.status
|
||||
@ -158,8 +158,9 @@ class Request {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (err.message.includes('timeout')) errMessage = t('axios.timeout')
|
||||
ElMessage({ message: errMessage, type: 'error' })
|
||||
err.message.includes('timeout') && (errMessage = t('axios.timeout'))
|
||||
err.code == 'ERR_NETWORK' && (errMessage = err.config.baseURL + t('axios.errNetwork'))
|
||||
errMessage && ElMessage({ message: errMessage, type: 'error' })
|
||||
}
|
||||
|
||||
private handleAuthError(code: number) {
|
||||
|
||||
@ -125,7 +125,7 @@
|
||||
}
|
||||
|
||||
const blurImageHeight = ()=> {
|
||||
diyStore.editComponent.imageHeight = parseFloat(diyStore.editComponent.imageHeight).toFixed(2);
|
||||
diyStore.editComponent.imageHeight = parseInt(diyStore.editComponent.imageHeight);
|
||||
}
|
||||
|
||||
const imageBoxRef = ref()
|
||||
|
||||
@ -155,6 +155,13 @@ const diyStore = useDiyStore()
|
||||
const route = useRoute();
|
||||
const router = useRouter()
|
||||
|
||||
route.query.id = route.query.id || 0;
|
||||
route.query.name = route.query.name || '';
|
||||
route.query.type = route.query.type || ''; // 页面模板,新页面传入
|
||||
route.query.template = route.query.template || ''; // 页面模板名称,新页面传入
|
||||
route.query.title = route.query.title || '';
|
||||
route.query.back = route.query.back || '/diy/list';
|
||||
|
||||
const wapUrl = ref('')
|
||||
const wapDomain = ref('')
|
||||
const wapPreview = ref('')
|
||||
@ -163,7 +170,7 @@ const loadingIframe = ref(false) // 加载iframe
|
||||
const loadingDev = ref(false) // 加载开发环境配置
|
||||
const timeFrame = ref(0)
|
||||
|
||||
const backPath = route.query.back || '/diy/list'
|
||||
const backPath = route.query.back
|
||||
const component = ref([])
|
||||
const componentType: string[] = reactive([])
|
||||
const page = ref('')
|
||||
@ -172,12 +179,6 @@ const siteId = ref(0)
|
||||
const activeNames = ref(componentType)
|
||||
const handleChange = (val: string[]) => { }
|
||||
|
||||
route.query.id = route.query.id || 0;
|
||||
route.query.name = route.query.name || '';
|
||||
route.query.template = route.query.template || ''; // 页面模板,新页面传入
|
||||
route.query.template_name = route.query.template_name || ''; // 页面模板名称,新页面传入
|
||||
route.query.title = route.query.title || '';
|
||||
|
||||
// 初始化原数据
|
||||
const originData = reactive({
|
||||
id: diyStore.id,
|
||||
@ -245,8 +246,8 @@ watch(
|
||||
initPage({
|
||||
id: route.query.id,
|
||||
name: route.query.name,
|
||||
type: route.query.type,
|
||||
template: route.query.template,
|
||||
template_name: route.query.template_name,
|
||||
title: route.query.title
|
||||
}).then(res => {
|
||||
let data = res.data;
|
||||
@ -255,6 +256,7 @@ initPage({
|
||||
diyStore.name = data.name;
|
||||
diyStore.type = data.type;
|
||||
diyStore.typeName = data.type_name;
|
||||
diyStore.templateName = data.template;
|
||||
diyStore.isDefault = data.is_default;
|
||||
if (data.value) {
|
||||
let sources = JSON.parse(data.value);
|
||||
@ -387,11 +389,13 @@ const save = () => {
|
||||
name: diyStore.name,
|
||||
title: diyStore.global.title,
|
||||
type: diyStore.type,
|
||||
template: diyStore.templateName,
|
||||
is_default: diyStore.isDefault,
|
||||
is_change: isChange.value ? 0 : 1,
|
||||
value: JSON.stringify({
|
||||
global: toRaw(diyStore.global),
|
||||
value: toRaw(diyStore.value)
|
||||
})
|
||||
}),
|
||||
};
|
||||
|
||||
const save = diyStore.id ? editDiyPage : addDiyPage
|
||||
|
||||
@ -1,196 +1,355 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="page-item relative bg-no-repeat mr-[40px] mt-[20px] bg-[#f7f7f7] w-[300px] pt-[80px] pb-[20px]"
|
||||
:class="{ 'cursor-pointer' : !item.isDisabledPop }"
|
||||
v-for="(item,key) in page"
|
||||
:key="key">
|
||||
<p class="absolute top-[46px] left-[50%] translate-x-[-50%] text-[14px] truncate w-[130px] text-center">
|
||||
{{item.use_template.title}}</p>
|
||||
|
||||
<div class="flex h-[700px] bg-body p-[20px]" v-show="loading">
|
||||
<iframe v-show="loadingIframe" class="w-[375px] border border-slate-100" :src="wapPreview" frameborder="0" id="previewIframe" @load="loadIframe"></iframe>
|
||||
<div v-show="loadingDev" class="w-[375px] border border-slate-100 pt-[20px] px-[20px]">
|
||||
<div class="font-bold text-xl mb-[40px]">{{t('developTitle')}}</div>
|
||||
<div class="mb-[20px] flex flex-col">
|
||||
<text class="mb-[10px]">{{ t('wapDomain') }}</text>
|
||||
<el-input v-model="wapDomain" :placeholder="t('wapDomainPlaceholder')" clearable />
|
||||
<div v-show="item.use_template.url" class="w-[282px] h-[500px] mx-auto">
|
||||
<iframe v-show="item.loadingIframe" class="w-[282px] h-[500px] mx-auto"
|
||||
:src="item.use_template.wapPreview"
|
||||
frameborder="0" @load="loadIframe(key)"></iframe>
|
||||
<div v-show="item.loadingDev"
|
||||
class="w-[282px] h-[500px] mx-auto bg-body pt-[20px] px-[20px]">
|
||||
<div class="font-bold text-xl mb-[40px]">{{t('developTitle')}}</div>
|
||||
<div class="mb-[20px] flex flex-col">
|
||||
<text class="mb-[10px]">{{ t('wapDomain') }}</text>
|
||||
<el-input v-model="wapDomain" :placeholder="t('wapDomainPlaceholder')" clearable/>
|
||||
</div>
|
||||
<el-button type="primary" @click="saveDomain()">{{ t('confirm') }}</el-button>
|
||||
</div>
|
||||
<el-button type="primary" @click="save" >{{ t('confirm') }}</el-button>
|
||||
</div>
|
||||
|
||||
<div class="w-[500px] ml-[40px]">
|
||||
<el-button type="primary" @click="toDecorate()">{{ t('decorate') }}</el-button>
|
||||
|
||||
<div class="info-wrap mt-[20px]">
|
||||
<el-form label-width="80px" class="px-[10px]">
|
||||
<el-form-item :label="t('preview')">
|
||||
<el-radio-group v-model="previewMode">
|
||||
<el-radio :label="'weapp'">{{t('weapp')}}</el-radio>
|
||||
<el-radio :label="'wechat'">{{t('wechat')}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<template v-if="previewMode == 'wechat'">
|
||||
<el-form-item :label="t('link')" v-show="wapPreview">
|
||||
<el-input readonly :value="wapPreview">
|
||||
<template #append>
|
||||
<el-button @click="copyEvent(wapPreview)" class="bg-primary copy">{{ t('copy') }}</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label=" " v-show="wapImage">
|
||||
<el-image :src="wapImage"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-if="previewMode == 'weapp'">
|
||||
<el-form-item label=" " v-if="weappConfig.qr_code">
|
||||
<el-image class="w-[100px] h-[100px]" :src="img(weappConfig.qr_code)"/>
|
||||
</el-form-item>
|
||||
<el-form-item label=" " v-else>
|
||||
<span class="text-gray-400">{{t('weappNotSet')}}</span>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-show="!item.use_template.wapPreview" class="overflow-hidden w-[282px] h-[500px] mx-auto">
|
||||
<img class="max-w-full" v-if="item.use_template.cover"
|
||||
:src="img(item.use_template.cover)"/>
|
||||
</div>
|
||||
|
||||
<p class="text-[12px] text-[#999] mt-[10px] mx-auto truncate text-center w-[250px]">
|
||||
{{item.use_template.desc}}</p>
|
||||
|
||||
<div class="item-hide absolute inset-x-0 inset-y-0 bg-black bg-opacity-50 text-center rounded-[40px]"
|
||||
:class="{ 'disabled' : item.isDisabledPop }">
|
||||
<div class="item-btn-box absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-col flex-wrap">
|
||||
<el-button @click="show(key,item)">切换</el-button>
|
||||
<el-button v-show="item.use_template.mode == 'diy'" @click="toDecorate(item.use_template)">装修
|
||||
</el-button>
|
||||
<el-button @click="toPreview(item.use_template)">预览</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="showDialog" :title="t('changeTemplate')" width="400px" :close-on-press-escape="false"
|
||||
:destroy-on-close="true" :close-on-click-modal="false">
|
||||
|
||||
<el-form :model="form" label-width="0px" v-if="formData.type">
|
||||
<el-form-item label="">
|
||||
<div>{{t('hopeBeforeTip')}}<span class="text-primary px-[5px]">{{ page[formData.type].title }}</span>{{t('hopeAfterTip')}}
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="">
|
||||
<el-select v-model="hope" class="w-full">
|
||||
<el-option :label="t('changeTemplateTip') + ' ' + page[formData.type].title + ' ' + t('template')"
|
||||
value="template"/>
|
||||
<el-option :label="t('changeMyPageTip') + ' ' + page[formData.type].title" value="diy"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="" v-show="hope == 'template'">
|
||||
<el-select v-model="formData.template" class="w-full">
|
||||
<el-option v-for="(item, key) in page[formData.type].template" :label="item.title" :value="key"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="" v-show="hope == 'diy'">
|
||||
<el-select v-model="formData.id" class="w-full">
|
||||
<el-option v-for="(item, index) in page[formData.type].my_page" :label="item.title"
|
||||
:value="item.id"/>
|
||||
</el-select>
|
||||
<div class="mt-[10px]">
|
||||
<span class="cursor-pointer text-primary mr-[10px]" @click="toDiyList">{{ t('createPage') }}</span>
|
||||
<span class="cursor-pointer text-primary" @click="refreshMyPage">{{ t('refreshPage') }}</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel')}}</el-button>
|
||||
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, reactive, watch} from 'vue'
|
||||
import {reactive, ref, watch, computed} from 'vue'
|
||||
import {t} from '@/lang'
|
||||
import { useRouter} from 'vue-router'
|
||||
import {getWeappConfig} from '@/api/weapp'
|
||||
import {getUrl} from '@/api/sys'
|
||||
import {useClipboard} from '@vueuse/core'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import {img} from '@/utils/common'
|
||||
import QRCode from "qrcode";
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {TabsPaneContext, ElMessage, ElMessageBox} from 'element-plus'
|
||||
import {getDecoratePage, getDiyList, changeTemplate} from '@/api/diy'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const wapUrl = ref('')
|
||||
const page: any = reactive({})
|
||||
const showDialog = ref(false)
|
||||
const router = useRouter()
|
||||
const hope = ref('template')
|
||||
const wapDomain = ref('')
|
||||
const wapImage = ref('')
|
||||
const wapPreview = ref('')
|
||||
|
||||
const loading = ref(false)
|
||||
const loadingIframe = ref(false) // 加载iframe
|
||||
const loadingDev = ref(false) // 加载开发环境配置
|
||||
const timeFrame = ref(0)
|
||||
// 添加自定义页面
|
||||
const formData = reactive({
|
||||
type: '',
|
||||
mode: '',
|
||||
template: '',
|
||||
id: ''
|
||||
})
|
||||
|
||||
var time = new Date().getTime();
|
||||
const refreshData = () => {
|
||||
formData.type = '';
|
||||
formData.mode = '';
|
||||
formData.template = '';
|
||||
formData.id = '';
|
||||
getDecoratePage({}).then((res => {
|
||||
for (let key in res.data) {
|
||||
page[key] = res.data[key]
|
||||
}
|
||||
|
||||
getUrl().then((res:any)=> {
|
||||
wapDomain.value = res.data.wap_domain;
|
||||
wapUrl.value = res.data.wap_url;
|
||||
setDomain();
|
||||
for (let key in page) {
|
||||
if (page[key].use_template.url) {
|
||||
page[key].loadingIframe = false; // 加载iframe
|
||||
page[key].loadingDev = false; // 加载开发环境配置
|
||||
page[key].isDisabledPop = false; // 是否禁止打开遮罩层
|
||||
|
||||
// 生产模式禁止
|
||||
if(import.meta.env.MODE == 'production') return;
|
||||
wapDomain.value = page[key].domain_url.wap_domain;
|
||||
page[key].wapUrl = page[key].domain_url.wap_url;
|
||||
|
||||
// env文件配置过wap域名
|
||||
if (wapDomain.value) return;
|
||||
// 开发模式情况下,并且未配置wap域名,则获取缓存域名
|
||||
if (import.meta.env.MODE == 'development' && !wapDomain.value && storage.get('wap_domain')) {
|
||||
page[key].wapUrl = storage.get('wap_domain')
|
||||
page[key].loadingIframe = true; // 加载iframe
|
||||
page[key].loadingDev = false; // 加载开发环境配置
|
||||
page[key].isDisabledPop = false; // 是否禁止打开遮罩层
|
||||
}
|
||||
|
||||
let wap_domain_storage = storage.get('wap_domain');
|
||||
if(wap_domain_storage){
|
||||
wapUrl.value = wap_domain_storage
|
||||
setDomain();
|
||||
return;
|
||||
}
|
||||
setDomain(key);
|
||||
page[key].timeFrame = new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
timeFrame.value = new Date().getTime();
|
||||
});
|
||||
|
||||
const save = ()=> {
|
||||
wapUrl.value = wapDomain.value + '/wap'
|
||||
setDomain();
|
||||
storage.set({ key : 'wap_domain', data :wapUrl.value });
|
||||
loadingIframe.value = true;
|
||||
loadingDev.value = false;
|
||||
}));
|
||||
}
|
||||
|
||||
const setDomain = ()=> {
|
||||
let siteInfo = storage.get('siteInfo');
|
||||
let siteId = 0;
|
||||
if (siteInfo) siteId = siteInfo.site_id;
|
||||
wapPreview.value = `${wapUrl.value}/pages/index/index?mode=preview&site_id=${siteId}`;
|
||||
QRCode.toDataURL(wapPreview.value, {errorCorrectionLevel: 'L', margin: 0, width: 100}).then(url => {
|
||||
wapImage.value = url
|
||||
refreshData();
|
||||
|
||||
// 监听iframe加载事件
|
||||
const loadIframe = (key: string) => {
|
||||
if (storage.get('wap_domain')) return;
|
||||
if (!page[key].use_template.wapPreview) return;
|
||||
|
||||
var loadTime = new Date().getTime();
|
||||
var difference = loadTime - page[key].timeFrame;
|
||||
// 检测页面加载差异,小于1000毫秒,则配置wap端域名
|
||||
if (difference < 1000) {
|
||||
page[key].loadingDev = true;
|
||||
page[key].isDisabledPop = true;
|
||||
page[key].loadingIframe = false;
|
||||
} else {
|
||||
page[key].loadingDev = false;
|
||||
page[key].isDisabledPop = false;
|
||||
page[key].loadingIframe = true;
|
||||
}
|
||||
}
|
||||
|
||||
const saveDomain = () => {
|
||||
let wapUrl = wapDomain.value + '/wap';
|
||||
storage.set({key: 'wap_domain', data: wapUrl});
|
||||
|
||||
for (let key in page) {
|
||||
if (page[key].use_template.url) {
|
||||
page[key].wapUrl = wapUrl;
|
||||
setDomain(key);
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
for (let key in page) {
|
||||
if (page[key].use_template.url) {
|
||||
page[key].loadingIframe = true; // 加载iframe
|
||||
page[key].loadingDev = false; // 加载开发环境配置
|
||||
page[key].isDisabledPop = false; // 是否禁止打开遮罩层
|
||||
}
|
||||
}
|
||||
}, 100 * 3);
|
||||
}
|
||||
|
||||
const setDomain = (key: string) => {
|
||||
page[key].use_template.wapPreview = page[key].wapUrl + page[key].use_template.url;
|
||||
}
|
||||
|
||||
const show = (key: string, data: any) => {
|
||||
// 每次打开时赋值
|
||||
showDialog.value = true;
|
||||
|
||||
hope.value = data.use_template.hope;
|
||||
formData.type = key;
|
||||
formData.mode = data.use_template.mode;
|
||||
|
||||
if (hope.value == 'template') {
|
||||
formData.template = data.use_template.template;
|
||||
} else if (hope.value == 'diy') {
|
||||
formData.id = data.use_template.id;
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转去装修
|
||||
const toDecorate = (data: any) => {
|
||||
let query: any = {
|
||||
back: '/diy/index'
|
||||
};
|
||||
if (data.id) {
|
||||
query.id = data.id;
|
||||
} else if (data.name) {
|
||||
query.name = data.name;
|
||||
}
|
||||
let url = router.resolve({
|
||||
path: '/decorate/edit',
|
||||
query
|
||||
});
|
||||
window.open(url.href);
|
||||
}
|
||||
|
||||
// 跳转去预览
|
||||
const toPreview = (data: any) => {
|
||||
let query: any = {};
|
||||
if (data.id) {
|
||||
query.id = data.id;
|
||||
} else if (data.name) {
|
||||
query.name = data.name;
|
||||
}
|
||||
let url = router.resolve({
|
||||
path: '/decorate/preview',
|
||||
query
|
||||
});
|
||||
window.open(url.href);
|
||||
}
|
||||
|
||||
// 创建微页面
|
||||
const toDiyList = (data: any) => {
|
||||
let url = router.resolve({
|
||||
path: '/site/diy/list'
|
||||
});
|
||||
window.open(url.href);
|
||||
}
|
||||
|
||||
// 刷新我的微页面
|
||||
const refreshMyPage = () => {
|
||||
getDiyList({type: formData.type, mode: 'diy'}).then((res) => {
|
||||
let isExist = true; // 检测选择的微页面是否存在,不存在则清空
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
if (formData.id == res.data[i].id) {
|
||||
isExist = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isExist) {
|
||||
formData.id = '';
|
||||
}
|
||||
page[formData.type].my_page = {};
|
||||
Object.assign(page[formData.type].my_page, res.data);
|
||||
})
|
||||
}
|
||||
|
||||
// 监听iframe加载事件
|
||||
const loadIframe = ()=> {
|
||||
if (!wapPreview.value) return
|
||||
var loadTime = new Date().getTime();
|
||||
var difference = loadTime - timeFrame.value;
|
||||
// 检测页面加载差异,小于1000毫秒,则配置wap端域名
|
||||
if (difference < 1000) {
|
||||
loadingDev.value = true;
|
||||
loadingIframe.value = false;
|
||||
wapPreview.value = ''
|
||||
wapImage.value = ''
|
||||
} else {
|
||||
loadingDev.value = false;
|
||||
loadingIframe.value = true;
|
||||
watch(
|
||||
() => hope.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue == 'template') {
|
||||
formData.id = '';
|
||||
} else if (newValue == 'diy') {
|
||||
formData.mode = 'diy';
|
||||
formData.template = '';
|
||||
}
|
||||
}
|
||||
loading.value = true
|
||||
)
|
||||
|
||||
watch(
|
||||
() => formData.template,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue) {
|
||||
formData.mode = page[formData.type].template[newValue].mode;
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const isRepeat = ref(false)
|
||||
const save = () => {
|
||||
if (hope.value == 'template') {
|
||||
if (formData.template == '') {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: `${t('placeholderTemplate')}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (hope.value == 'diy') {
|
||||
if (formData.id == '') {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: `${t('placeholderMyPage')}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRepeat.value) return
|
||||
isRepeat.value = true
|
||||
|
||||
changeTemplate({
|
||||
...formData
|
||||
}).then((res) => {
|
||||
isRepeat.value = false;
|
||||
showDialog.value = false;
|
||||
refreshData();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const weappConfig = reactive({
|
||||
qr_code: ''
|
||||
})
|
||||
|
||||
const previewMode = ref('weapp')
|
||||
|
||||
/**
|
||||
* 获取微信配置
|
||||
*/
|
||||
getWeappConfig().then((res: any) => {
|
||||
if (res.code == 1) {
|
||||
let data = res.data;
|
||||
weappConfig.qr_code = data.qr_code;
|
||||
}
|
||||
})
|
||||
|
||||
const url = router.resolve({
|
||||
path: '/decorate/edit',
|
||||
query: {name: 'DIY_INDEX'}
|
||||
});
|
||||
|
||||
const toDecorate = () => {
|
||||
router.push('/decorate/edit?name=DIY_INDEX&back=/diy/index')
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制
|
||||
*/
|
||||
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'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.copy {
|
||||
background: var(--el-color-primary) !important;
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.page-item {
|
||||
|
||||
background-image: url(assets/images/iphone_bg.png);
|
||||
background-color: var(--el-bg-color);
|
||||
background-size: 100%;
|
||||
|
||||
.item-hide {
|
||||
display: none;
|
||||
|
||||
.item-btn-box {
|
||||
|
||||
button {
|
||||
height: 35px;
|
||||
width: 100px;
|
||||
|
||||
& ~ button {
|
||||
margin-top: 15px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.item-hide:not(.disabled) {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -9,14 +9,14 @@
|
||||
</div>
|
||||
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="diyPageTableData.searchParam" ref="searchFormDiyPageRef" v-if="tabValue == 'diy'">
|
||||
<el-form :inline="true" :model="diyPageTableData.searchParam" ref="searchFormDiyPageRef">
|
||||
<el-form-item :label="t('title')" prop="title">
|
||||
<el-input v-model="diyPageTableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('typeName')" prop="type">
|
||||
<el-select v-model="diyPageTableData.searchParam.type" :placeholder="t('pageTemplatePlaceholder')">
|
||||
<el-select v-model="diyPageTableData.searchParam.type" :placeholder="t('pageTypePlaceholder')">
|
||||
<el-option :label="t('all')" value="" />
|
||||
<el-option v-for="(item, key) in pageTemplate" :label="item.title" :value="key" />
|
||||
<el-option v-for="(item, key) in pageType" :label="item.title" :value="key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@ -24,84 +24,40 @@
|
||||
<el-button @click="resetForm(searchFormDiyPageRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form :inline="true" :model="diyRouteTableData.searchParam" ref="searchFormDiyRouteRef" v-if="tabValue == 'route'">
|
||||
<el-form-item :label="t('title')" prop="title">
|
||||
<el-input v-model="diyRouteTableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadDiyRouteList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormDiyRouteRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div>
|
||||
<el-tabs v-model="tabValue" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane :label="t('diyPage')" name="diy">
|
||||
<el-table :data="diyPageTableData.data" size="large" v-loading="diyPageTableData.loading">
|
||||
|
||||
<el-table :data="diyPageTableData.data" size="large" v-loading="diyPageTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !diyPageTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !diyPageTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="title" :label="t('title')" min-width="120" />
|
||||
<el-table-column prop="type_name" :label="t('typeName')" min-width="80" />
|
||||
<!-- <el-table-column :label="t('status')" min-width="80">-->
|
||||
<!-- <template #default="{ row }">-->
|
||||
<!-- <span v-if="row.type == 'DIY_PAGE'">-</span>-->
|
||||
<!-- <template v-else>-->
|
||||
<!-- <span v-if="row.is_default == 1" class="text-primary">{{ t('isUse') }}</span>-->
|
||||
<!-- <span v-else>{{ t('unused') }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column prop="update_time" :label="t('updateTime')" min-width="120" />
|
||||
|
||||
<el-table-column prop="title" :label="t('title')" min-width="120" />
|
||||
<el-table-column prop="type_name" :label="t('typeName')" min-width="80" />
|
||||
<el-table-column :label="t('status')" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.type == 'DIY_PAGE'">-</span>
|
||||
<template v-else>
|
||||
<span v-if="row.is_default == 1" class="text-primary">{{ t('isUse') }}</span>
|
||||
<span v-else>{{ t('unused') }}</span>
|
||||
</template>
|
||||
</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="160">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="promoteEvent(row)">{{ t('promote') }}</el-button>
|
||||
<el-button v-if="row.type != 'DIY_PAGE' && row.is_default == 0" type="primary" link @click="setUse(row)">{{ t('use') }}</el-button>
|
||||
<el-button v-if="row.type == 'DIY_PAGE'" type="primary" link @click="openShare(row)">{{ t('shareSet') }}</el-button>
|
||||
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||
<el-button v-if="row.type == 'DIY_PAGE' || row.is_default == 0" type="danger" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="diyPageTableData.page" v-model:page-size="diyPageTableData.limit" layout="total, sizes, prev, pager, next, jumper" :total="diyPageTableData.total" @size-change="loadDiyPageList()" @current-change="loadDiyPageList" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- 基础页面路径 -->
|
||||
<el-tab-pane :label="t('basicRoute')" name="route">
|
||||
<el-table :data="diyRouteTableData.data" size="large" v-loading="diyRouteTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !diyRouteTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="title" :label="t('title')" min-width="70" />
|
||||
<el-table-column prop="page" :label="t('wapUrl')" min-width="170">
|
||||
<template #default="{ row }">
|
||||
<span class="mr-[10px]">{{ wapDomain + row.page }}</span>
|
||||
<el-button type="primary" link @click="copyEvent(wapDomain + row.page)">{{ t('copy') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="page" :label="t('weappUrl')" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<span class="mr-[10px]">{{ row.page }}</span>
|
||||
<el-button type="primary" link @click="copyEvent(row.page)">{{ t('copy') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('share')" fixed="right" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.is_share == 1" type="primary" link @click="openShare(row)">{{ t('shareSet') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
</el-tabs>
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="toPreview(row)">{{ t('promote') }}</el-button>
|
||||
<el-button v-if="row.type == 'DIY_PAGE'" type="primary" link @click="openShare(row)">{{ t('shareSet') }}</el-button>
|
||||
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||
<!-- <el-button v-if="row.type == 'DIY_PAGE' || row.is_default == 0" type="danger" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>-->
|
||||
<el-button type="danger" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="diyPageTableData.page" v-model:page-size="diyPageTableData.limit" layout="total, sizes, prev, pager, next, jumper" :total="diyPageTableData.total" @size-change="loadDiyPageList()" @current-change="loadDiyPageList" />
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
@ -113,16 +69,16 @@
|
||||
<el-form-item :label="t('title')" prop="title">
|
||||
<el-input v-model="formData.title" :placeholder="t('titlePlaceholder')" clearable maxlength="12" show-word-limit class="w-full" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('typeName')" prop="template">
|
||||
<el-select v-model="formData.template" :placeholder="t('pageTemplatePlaceholder')" class="w-full">
|
||||
<el-option v-for="(item, key) in pageTemplate" :label="item.title" :value="key" />
|
||||
<el-form-item :label="t('addType')" prop="type">
|
||||
<el-select v-model="formData.type" :placeholder="t('pageTypePlaceholder')" class="w-full">
|
||||
<el-option v-for="(item, key) in pageType" :label="item.title" :value="key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('templateName')" prop="template_name" v-show="pageTemplateData">
|
||||
<el-radio-group v-model="formData.template_name">
|
||||
<el-radio label="">{{ t('empty') }}</el-radio>
|
||||
<el-radio v-for="(item,key) in pageTemplateData" :label="key">{{item.title}}</el-radio>
|
||||
</el-radio-group>
|
||||
<el-form-item :label="t('templateName')" prop="template" v-show="pageTypeData">
|
||||
<el-select v-model="formData.template" class="w-full">
|
||||
<el-option :label="t('emptyTemplate')" value="" />
|
||||
<el-option v-for="(item, key) in pageTypeData" :label="item.title" :value="key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@ -136,7 +92,6 @@
|
||||
|
||||
<!-- 分享设置-->
|
||||
<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>
|
||||
@ -164,345 +119,217 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 推广-->
|
||||
<el-dialog v-model="promoteDialogVisible" :title="t('promote')" width="30%">
|
||||
<el-form label-width="90px">
|
||||
<el-form-item :label="t('shareLink')">
|
||||
<el-input readonly :value="promoteWapDomain">
|
||||
<template #append>
|
||||
<el-button @click="copyEvent(promoteWapDomain)" class="bg-primary copy">{{ t('copy') }}</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label=" ">
|
||||
<el-image :src="wapImage" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- <el-tabs v-model="tabPromote">-->
|
||||
<!-- <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-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, watch, computed } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getDiyPageList, deleteDiyPage, setUseDiyPage, getDiyTemplate, getDiyRouteList, getDiyRouteInfo, editDiyPageShare, editDiyRouteShare } from '@/api/diy'
|
||||
import { TabsPaneContext, ElMessage, ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import QRCode from "qrcode";
|
||||
import { getUrl } from '@/api/sys'
|
||||
import { reactive, ref, watch, computed } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getDiyPageList, deleteDiyPage, getDiyTemplate, editDiyPageShare } from '@/api/diy'
|
||||
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import QRCode from "qrcode";
|
||||
import { getUrl } from '@/api/sys'
|
||||
|
||||
const pageTemplate: any = reactive({})
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title;
|
||||
const pageType: any = reactive({}) // 页面模板类型
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title;
|
||||
|
||||
// 添加自定义页面
|
||||
const formData = reactive({
|
||||
title: '',
|
||||
template: '',
|
||||
template_name:''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
template: [
|
||||
{ required: true, message: t('pageTemplatePlaceholder'), trigger: 'blur' },
|
||||
],
|
||||
title: [
|
||||
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const pageTemplateData = computed(()=>{
|
||||
let data:any = '';
|
||||
formData.template_name = '';
|
||||
if(formData.template){
|
||||
data = pageTemplate[formData.template].template;
|
||||
// 默认选中第一个
|
||||
// if(Object.keys(data).length) formData.template_name =Object.keys(data)[0];
|
||||
}
|
||||
return data;
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogVisible = ref(false)
|
||||
const addEvent = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
router.push(`/decorate/edit?template=${formData.template}&template_name=${formData.template_name}&title=${formData.title}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let diyRouteTableData = reactive({
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
"title": "",
|
||||
"type":""
|
||||
}
|
||||
})
|
||||
|
||||
const wapDomain = ref('')
|
||||
const getDomain = async () => {
|
||||
wapDomain.value = (await getUrl()).data.wap_url;
|
||||
};
|
||||
getDomain();
|
||||
|
||||
/**
|
||||
* 获取自定义路由列表
|
||||
*/
|
||||
const loadDiyRouteList = () => {
|
||||
diyRouteTableData.loading = true
|
||||
|
||||
getDiyRouteList({
|
||||
...diyRouteTableData.searchParam
|
||||
}).then(res => {
|
||||
diyRouteTableData.loading = false
|
||||
diyRouteTableData.data = res.data
|
||||
}).catch(() => {
|
||||
diyRouteTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadDiyRouteList()
|
||||
|
||||
// 获取自定义页面模板
|
||||
getDiyTemplate({}).then(res => {
|
||||
for (let key in res.data) {
|
||||
pageTemplate[key] = res.data[key]
|
||||
}
|
||||
})
|
||||
|
||||
let diyPageTableData: any = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
"title": "",
|
||||
"template": '',
|
||||
}
|
||||
})
|
||||
|
||||
const tabValue = ref('diy')
|
||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||
tabValue.value = tab.props.name;
|
||||
if (tabValue.value == 'diy') {
|
||||
loadDiyPageList()
|
||||
} else {
|
||||
loadDiyRouteList()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const searchFormDiyRouteRef = ref<FormInstance>()
|
||||
const searchFormDiyPageRef = ref<FormInstance>()
|
||||
|
||||
// 获取自定义页面列表
|
||||
const loadDiyPageList = (page: number = 1) => {
|
||||
diyPageTableData.loading = true
|
||||
diyPageTableData.page = page
|
||||
|
||||
getDiyPageList({
|
||||
page: diyPageTableData.page,
|
||||
limit: diyPageTableData.limit,
|
||||
...diyPageTableData.searchParam
|
||||
}).then(res => {
|
||||
diyPageTableData.loading = false
|
||||
diyPageTableData.data = res.data.data
|
||||
diyPageTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
diyPageTableData.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
loadDiyPageList()
|
||||
|
||||
// 编辑自定义页面
|
||||
const editEvent = (data: any) => {
|
||||
let url = router.resolve({
|
||||
path: '/decorate/edit',
|
||||
query: { id: data.id }
|
||||
});
|
||||
window.open(url.href);
|
||||
}
|
||||
|
||||
// 删除自定义页面
|
||||
const deleteEvent = (id: number) => {
|
||||
ElMessageBox.confirm(t('diyPageDeleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
}
|
||||
).then(() => {
|
||||
deleteDiyPage(id).then(() => {
|
||||
loadDiyPageList()
|
||||
}).catch(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 设为使用
|
||||
const setUse = (data: any) => {
|
||||
setUseDiyPage({ id: data.id }).then(() => {
|
||||
loadDiyPageList()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制
|
||||
*/
|
||||
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'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const tabShareType = ref('wechat')
|
||||
const sharePage = ref('')
|
||||
const shareFormId = ref(0)
|
||||
const diyRouteData = reactive({
|
||||
title: '',
|
||||
name: '',
|
||||
page: '',
|
||||
is_share: 0,
|
||||
sort: 0
|
||||
})
|
||||
const shareFormData = reactive({
|
||||
wechat: {
|
||||
// 添加自定义页面
|
||||
const formData = reactive({
|
||||
title: '',
|
||||
desc: '',
|
||||
url: ''
|
||||
},
|
||||
weapp: {
|
||||
title: '',
|
||||
url: ''
|
||||
}
|
||||
})
|
||||
const shareDialogVisible = ref(false)
|
||||
const shareFormRules = computed(() => {
|
||||
return {}
|
||||
})
|
||||
type: '',
|
||||
template:''
|
||||
})
|
||||
|
||||
const shareFormRef = ref<FormInstance>()
|
||||
const openShare = async (row: any) => {
|
||||
if (tabValue.value == 'route') {
|
||||
// 基础页面
|
||||
let info = (await getDiyRouteInfo({
|
||||
name: row.name
|
||||
})).data;
|
||||
|
||||
if (info.title) {
|
||||
row.id = info.id;
|
||||
row.title = info.title
|
||||
row.name = info.name
|
||||
row.page = info.page
|
||||
row.is_share = info.is_share
|
||||
row.sort = info.sort
|
||||
row.share = info.share
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
title: [
|
||||
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: t('pageTypePlaceholder'), trigger: 'blur' },
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
diyRouteData.title = row.title
|
||||
diyRouteData.name = row.name
|
||||
diyRouteData.page = row.page
|
||||
diyRouteData.is_share = row.is_share
|
||||
diyRouteData.sort = row.sort
|
||||
const pageTypeData = computed(()=>{
|
||||
let data:any = '';
|
||||
formData.template = '';
|
||||
if(formData.type){
|
||||
data = pageType[formData.type].template;
|
||||
}
|
||||
return data;
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogVisible = ref(false)
|
||||
const addEvent = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
let url = `/decorate/edit?type=${formData.type}&title=${formData.title}`;
|
||||
if(formData.template) url += `&template=${formData.template}`;
|
||||
router.push(url);
|
||||
}
|
||||
})
|
||||
}
|
||||
shareFormId.value = row.id;
|
||||
sharePage.value = row.title;
|
||||
let share = row.share ? JSON.parse(row.share) : {
|
||||
wechat: { title: '', desc: '', url: '' },
|
||||
weapp: { title: '', url: '' }
|
||||
|
||||
const wapDomain = ref('')
|
||||
const getDomain = async () => {
|
||||
wapDomain.value = (await getUrl()).data.wap_url;
|
||||
};
|
||||
if (share) {
|
||||
shareFormData.wechat = share.wechat;
|
||||
shareFormData.weapp = share.weapp;
|
||||
getDomain();
|
||||
|
||||
// 获取自定义页面模板
|
||||
getDiyTemplate({ mode : 'diy' }).then(res => {
|
||||
for (let key in res.data) {
|
||||
pageType[key] = res.data[key]
|
||||
}
|
||||
})
|
||||
|
||||
let diyPageTableData: any = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
"title": "",
|
||||
"template": '',
|
||||
}
|
||||
})
|
||||
|
||||
const searchFormDiyPageRef = ref<FormInstance>()
|
||||
|
||||
// 获取自定义页面列表
|
||||
const loadDiyPageList = (page: number = 1) => {
|
||||
diyPageTableData.loading = true
|
||||
diyPageTableData.page = page
|
||||
|
||||
getDiyPageList({
|
||||
page: diyPageTableData.page,
|
||||
limit: diyPageTableData.limit,
|
||||
...diyPageTableData.searchParam
|
||||
}).then(res => {
|
||||
diyPageTableData.loading = false
|
||||
diyPageTableData.data = res.data.data
|
||||
diyPageTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
diyPageTableData.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
shareDialogVisible.value = true;
|
||||
}
|
||||
loadDiyPageList()
|
||||
|
||||
const shareEvent = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
// 编辑自定义页面
|
||||
const editEvent = (data: any) => {
|
||||
let url = router.resolve({
|
||||
path: '/decorate/edit',
|
||||
query: {id: data.id}
|
||||
});
|
||||
window.open(url.href);
|
||||
}
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
let save = tabValue.value == 'diy' ? editDiyPageShare : editDiyRouteShare
|
||||
save({
|
||||
id: shareFormId.value,
|
||||
share: JSON.stringify(shareFormData),
|
||||
...diyRouteData
|
||||
}).then(() => {
|
||||
if (tabValue.value == 'diy') {
|
||||
loadDiyPageList()
|
||||
} else {
|
||||
loadDiyRouteList()
|
||||
// 删除自定义页面
|
||||
const deleteEvent = (id: number) => {
|
||||
ElMessageBox.confirm(t('diyPageDeleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
}
|
||||
shareDialogVisible.value = false;
|
||||
).then(() => {
|
||||
deleteDiyPage(id).then(() => {
|
||||
loadDiyPageList()
|
||||
}).catch(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转去预览
|
||||
const toPreview = (data: any) => {
|
||||
let query: any = {};
|
||||
if (data.id) {
|
||||
query.id = data.id;
|
||||
} else if (data.name) {
|
||||
query.name = data.name;
|
||||
}
|
||||
let url = router.resolve({
|
||||
path: '/decorate/preview',
|
||||
query
|
||||
});
|
||||
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 promoteDialogVisible = ref(false)
|
||||
const tabPromote = ref('wechat')
|
||||
const promoteWapDomain = ref('')
|
||||
const wapImage = ref('')
|
||||
const promoteEvent = (data: any) => {
|
||||
promoteWapDomain.value = wapDomain.value + '/pages/index/diy?id=' + data.id;
|
||||
QRCode.toDataURL(promoteWapDomain.value, { errorCorrectionLevel: 'L', margin: 0, width: 100 }).then(url => {
|
||||
wapImage.value = url
|
||||
const shareDialogVisible = ref(false)
|
||||
const shareFormRules = computed(() => {
|
||||
return {}
|
||||
})
|
||||
|
||||
promoteDialogVisible.value = true;
|
||||
}
|
||||
const shareFormRef = ref<FormInstance>()
|
||||
const openShare = async (row: any) => {
|
||||
shareFormId.value = row.id;
|
||||
sharePage.value = row.title;
|
||||
let share = row.share ? JSON.parse(row.share) : {
|
||||
wechat: { title: '', desc: '', url: '' },
|
||||
weapp: { title: '', url: '' }
|
||||
};
|
||||
if (share) {
|
||||
shareFormData.wechat = share.wechat;
|
||||
shareFormData.weapp = share.weapp;
|
||||
}
|
||||
|
||||
shareDialogVisible.value = true;
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined)=>{
|
||||
if (!formEl) return
|
||||
formEl.resetFields();
|
||||
loadDiyPageList();
|
||||
}
|
||||
const shareEvent = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
editDiyPageShare({
|
||||
id: shareFormId.value,
|
||||
share: JSON.stringify(shareFormData),
|
||||
}).then(() => {
|
||||
loadDiyPageList()
|
||||
shareDialogVisible.value = false;
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined)=>{
|
||||
if (!formEl) return
|
||||
formEl.resetFields();
|
||||
loadDiyPageList();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.copy {
|
||||
background: var(--el-color-primary) !important;
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
.copy {
|
||||
background: var(--el-color-primary) !important;
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
@ -1,18 +0,0 @@
|
||||
<template>
|
||||
<div class="main-container"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref} from 'vue'
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const url = router.resolve({
|
||||
path: '/decorate/edit',
|
||||
query: {name: 'DIY_MEMBER_INDEX'}
|
||||
});
|
||||
router.push('/decorate/edit?name=DIY_MEMBER_INDEX')
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
196
admin/src/views/diy/preview.vue
Normal file
196
admin/src/views/diy/preview.vue
Normal file
@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div class="main-container w-[375px] mx-auto my-[20px] relative">
|
||||
|
||||
<div class="flex h-full" v-show="loading">
|
||||
<iframe v-show="loadingIframe" class="w-[375px] border border-slate-100 bg-gray-100" :src="wapPreview" frameborder="0" id="previewIframe" @load="loadIframe"></iframe>
|
||||
<div v-show="loadingDev" class="w-[375px] border border-slate-100 bg-body pt-[20px] px-[20px]">
|
||||
<div class="font-bold text-xl mb-[40px]">{{t('developTitle')}}</div>
|
||||
<div class="mb-[20px] flex flex-col">
|
||||
<text class="mb-[10px]">{{ t('wapDomain') }}</text>
|
||||
<el-input v-model="wapDomain" :placeholder="t('wapDomainPlaceholder')" clearable/>
|
||||
</div>
|
||||
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
|
||||
</div>
|
||||
|
||||
<div class="w-[400px] absolute bg-body top-[10%] -right-[450px]" v-if="loadingIframe">
|
||||
|
||||
<div class="info-wrap mt-[20px]">
|
||||
<el-form label-width="40px" class="px-[20px]">
|
||||
<el-form-item :label="t('preview')">
|
||||
<el-radio-group v-model="previewMode">
|
||||
<el-radio :label="'weapp'">{{t('weapp')}}</el-radio>
|
||||
<el-radio :label="'wechat'">{{t('wechat')}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<template v-if="previewMode == 'wechat'">
|
||||
<el-form-item :label="t('link')" v-show="wapPreview">
|
||||
<el-input readonly :value="wapPreview">
|
||||
<template #append>
|
||||
<el-button @click="copyEvent(wapPreview)" class="bg-primary copy">{{ t('copy') }}</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label=" " v-show="wapImage">
|
||||
<el-image :src="wapImage"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-if="previewMode == 'weapp'">
|
||||
<el-form-item label=" " v-if="weappConfig.qr_code">
|
||||
<el-image class="w-[100px] h-[100px]" :src="img(weappConfig.qr_code)"/>
|
||||
</el-form-item>
|
||||
<el-form-item label=" " v-else>
|
||||
<span class="text-gray-400">{{t('weappNotSet')}}</span>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, reactive, watch} from 'vue'
|
||||
import {t} from '@/lang'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {getWeappConfig} from '@/api/weapp'
|
||||
import {getUrl} from '@/api/sys'
|
||||
import {useClipboard} from '@vueuse/core'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import {img} from '@/utils/common'
|
||||
import QRCode from "qrcode";
|
||||
import storage from '@/utils/storage'
|
||||
import {getPreviewData} from '@/api/diy'
|
||||
|
||||
const wapUrl = ref('')
|
||||
const wapDomain = ref('')
|
||||
const wapImage = ref('')
|
||||
const wapPreview = ref('')
|
||||
|
||||
const loading = ref(false)
|
||||
const loadingIframe = ref(false) // 加载iframe
|
||||
const loadingDev = ref(false) // 加载开发环境配置
|
||||
const timeFrame = ref(0)
|
||||
|
||||
var time = new Date().getTime();
|
||||
const route = useRoute();
|
||||
route.query.id = route.query.id || 0;
|
||||
route.query.name = route.query.name || '';
|
||||
|
||||
getUrl().then((res: any) => {
|
||||
wapDomain.value = res.data.wap_domain;
|
||||
wapUrl.value = res.data.wap_url;
|
||||
setDomain();
|
||||
|
||||
// 生产模式禁止
|
||||
if (import.meta.env.MODE == 'production') return;
|
||||
|
||||
// env文件配置过wap域名
|
||||
if (wapDomain.value) return;
|
||||
|
||||
let wap_domain_storage = storage.get('wap_domain');
|
||||
if (wap_domain_storage) {
|
||||
wapUrl.value = wap_domain_storage
|
||||
setDomain();
|
||||
return;
|
||||
}
|
||||
|
||||
timeFrame.value = new Date().getTime();
|
||||
});
|
||||
|
||||
const save = () => {
|
||||
wapUrl.value = wapDomain.value + '/wap';
|
||||
setDomain();
|
||||
storage.set({key: 'wap_domain', data: wapUrl.value});
|
||||
loadingIframe.value = true;
|
||||
loadingDev.value = false;
|
||||
}
|
||||
|
||||
const setDomain = () => {
|
||||
let siteInfo = storage.get('siteInfo');
|
||||
let siteCode = '';
|
||||
if (siteInfo) siteCode = siteInfo.site_code;
|
||||
|
||||
getPreviewData({
|
||||
id: route.query.id,
|
||||
name: route.query.name,
|
||||
}).then((res: any) => {
|
||||
let data = res.data;
|
||||
wapPreview.value = `${wapUrl.value}/${data.page}&mode=preview&site_id=${siteCode}`;
|
||||
QRCode.toDataURL(wapPreview.value, {errorCorrectionLevel: 'L', margin: 0, width: 100}).then(url => {
|
||||
wapImage.value = url
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 监听iframe加载事件
|
||||
const loadIframe = () => {
|
||||
if (!wapPreview.value) return;
|
||||
var loadTime = new Date().getTime();
|
||||
var difference = loadTime - timeFrame.value;
|
||||
// 检测页面加载差异,小于1000毫秒,则配置wap端域名
|
||||
if (difference < 1000) {
|
||||
loadingDev.value = true;
|
||||
loadingIframe.value = false;
|
||||
wapPreview.value = '';
|
||||
wapImage.value = '';
|
||||
} else {
|
||||
loadingDev.value = false;
|
||||
loadingIframe.value = true;
|
||||
}
|
||||
loading.value = true
|
||||
}
|
||||
|
||||
const weappConfig = reactive({
|
||||
qr_code: ''
|
||||
})
|
||||
|
||||
const previewMode = ref('weapp')
|
||||
|
||||
// 获取微信配置
|
||||
getWeappConfig().then((res: any) => {
|
||||
if (res.code == 1) {
|
||||
let data = res.data;
|
||||
weappConfig.qr_code = data.qr_code;
|
||||
}
|
||||
})
|
||||
|
||||
// 复制
|
||||
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'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
body {
|
||||
background: #edf0f3;
|
||||
}
|
||||
|
||||
.copy {
|
||||
background: var(--el-color-primary) !important;
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
258
admin/src/views/diy/route.vue
Normal file
258
admin/src/views/diy/route.vue
Normal file
@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-[24px]">{{pageName}}</span>
|
||||
</div>
|
||||
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="diyRouteTableData.searchParam" ref="searchFormDiyRouteRef">
|
||||
<el-form-item :label="t('title')" prop="title">
|
||||
<el-input v-model="diyRouteTableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadDiyRouteList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormDiyRouteRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-table :data="diyRouteTableData.data" size="large" v-loading="diyRouteTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !diyRouteTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="title" :label="t('title')" min-width="70" />
|
||||
<el-table-column prop="page" :label="t('wapUrl')" min-width="170">
|
||||
<template #default="{ row }">
|
||||
<span class="mr-[10px]">{{ wapDomain + row.page }}</span>
|
||||
<el-button type="primary" link @click="copyEvent(wapDomain + row.page)">{{ t('copy') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="page" :label="t('weappUrl')" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<span class="mr-[10px]">{{ row.page }}</span>
|
||||
<el-button type="primary" link @click="copyEvent(row.page)">{{ t('copy') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('share')" fixed="right" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.is_share == 1" type="primary" link @click="openShare(row)">{{ t('shareSet') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</el-card>
|
||||
|
||||
<!-- 分享设置-->
|
||||
<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="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="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>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, watch, computed } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getDiyTemplate, getDiyRouteList, getDiyRouteInfo, editDiyRouteShare } from '@/api/diy'
|
||||
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import QRCode from "qrcode";
|
||||
import { getUrl } from '@/api/sys'
|
||||
|
||||
const pageTemplate: any = reactive({})
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title;
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
let diyRouteTableData = reactive({
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
"title": "",
|
||||
"type":""
|
||||
}
|
||||
})
|
||||
|
||||
const wapDomain = ref('')
|
||||
const getDomain = async () => {
|
||||
wapDomain.value = (await getUrl()).data.wap_url;
|
||||
};
|
||||
getDomain();
|
||||
|
||||
/**
|
||||
* 获取自定义路由列表
|
||||
*/
|
||||
const loadDiyRouteList = () => {
|
||||
diyRouteTableData.loading = true
|
||||
|
||||
getDiyRouteList({
|
||||
...diyRouteTableData.searchParam
|
||||
}).then(res => {
|
||||
diyRouteTableData.loading = false
|
||||
diyRouteTableData.data = res.data
|
||||
}).catch(() => {
|
||||
diyRouteTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadDiyRouteList()
|
||||
|
||||
// 获取自定义页面模板
|
||||
getDiyTemplate({}).then(res => {
|
||||
for (let key in res.data) {
|
||||
pageTemplate[key] = res.data[key]
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const searchFormDiyRouteRef = ref<FormInstance>()
|
||||
|
||||
/**
|
||||
* 复制
|
||||
*/
|
||||
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'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const tabShareType = ref('wechat')
|
||||
const sharePage = ref('')
|
||||
const shareFormId = ref(0)
|
||||
const diyRouteData = reactive({
|
||||
title: '',
|
||||
name: '',
|
||||
page: '',
|
||||
is_share: 0,
|
||||
sort: 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) => {
|
||||
// 基础页面
|
||||
let info = (await getDiyRouteInfo({
|
||||
name: row.name
|
||||
})).data;
|
||||
|
||||
if (info.title) {
|
||||
row.id = info.id;
|
||||
row.title = info.title
|
||||
row.name = info.name
|
||||
row.page = info.page
|
||||
row.is_share = info.is_share
|
||||
row.sort = info.sort
|
||||
row.share = info.share
|
||||
}
|
||||
|
||||
diyRouteData.title = row.title
|
||||
diyRouteData.name = row.name
|
||||
diyRouteData.page = row.page
|
||||
diyRouteData.is_share = row.is_share
|
||||
diyRouteData.sort = row.sort
|
||||
|
||||
shareFormId.value = row.id;
|
||||
sharePage.value = row.title;
|
||||
let share = row.share ? JSON.parse(row.share) : {
|
||||
wechat: {title: '', desc: '', url: ''},
|
||||
weapp: {title: '', url: ''}
|
||||
};
|
||||
if (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) {
|
||||
let save = editDiyRouteShare
|
||||
save({
|
||||
id: shareFormId.value,
|
||||
share: JSON.stringify(shareFormData),
|
||||
...diyRouteData
|
||||
}).then(() => {
|
||||
loadDiyRouteList()
|
||||
shareDialogVisible.value = false;
|
||||
}).catch(() => {
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined)=>{
|
||||
if (!formEl) return
|
||||
formEl.resetFields();
|
||||
loadDiyRouteList();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.copy {
|
||||
background: var(--el-color-primary) !important;
|
||||
color: var(--el-color-white) !important;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
283
admin/src/views/finance/account.vue
Normal file
283
admin/src/views/finance/account.vue
Normal file
@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-[24px]">{{pageName}}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none base-bg !px-[35px]" shadow="never">
|
||||
<el-row class="flex">
|
||||
<el-col :span="8" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic :value="accountStat.pay ? Number.parseFloat(accountStat.pay).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('totalPay') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic :value="accountStat.refund ? Number.parseFloat(accountStat.refund).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('totalRefund') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic :value="accountStat.transfer ? Number.parseFloat(accountStat.transfer).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('totalTransfer') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="siteAccountLogTable.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('type')" class="items-center">
|
||||
<el-select v-model="siteAccountLogTable.searchParam.type" class="m-2" :placeholder="t('accountType')" >
|
||||
<el-option
|
||||
v-for="(item, index) in accountType"
|
||||
:key="index"
|
||||
:label="item"
|
||||
:value="index"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('tradeNo')" prop="trade_no">
|
||||
<el-input v-model="siteAccountLogTable.searchParam.trade_no" :placeholder="t('tradeNoPlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('createTime')" prop="create_time">
|
||||
<el-date-picker v-model="siteAccountLogTable.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="loadSiteAccountLogList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="siteAccountLogTable.data" size="large" v-loading="siteAccountLogTable.loading">
|
||||
<template #empty>
|
||||
<span>{{ !siteAccountLogTable.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="trade_no" :label="t('tradeNo')" min-width="120" />
|
||||
<el-table-column prop= "type_name" :label="t('type')" min-width="120" />
|
||||
<el-table-column prop="money" :label="t('money')" min-width="120" align="right" />
|
||||
<el-table-column :label="t('createTime')" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.create_time || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('operation')" fixed="right" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="detailEvent(row)">{{ t('detail') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="siteAccountLogTable.page" v-model:page-size="siteAccountLogTable.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="siteAccountLogTable.total"
|
||||
@size-change="loadSiteAccountLogList()" @current-change="loadSiteAccountLogList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-dialog v-model="showDialog" :title="t('accountDetail')" width="550px" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="110px" ref="formRef" class="page-form">
|
||||
|
||||
<el-form-item :label="t('tradeNo')" >
|
||||
<div class="input-width"> {{ formData.trade_no }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('type')" >
|
||||
<div class="input-width"> {{ formData.type_name }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('money')" >
|
||||
<div class="input-width"> {{ formData.money }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('createTime')" >
|
||||
<div class="input-width"> {{ formData.create_time }} </div>
|
||||
</el-form-item>
|
||||
<div v-if="formData.type == 'transfer'">
|
||||
<el-form-item :label="t('transferNo')" >
|
||||
<div class="input-width"> {{ formData.pay_info.transfer_no }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('transferTime')" >
|
||||
<div class="input-width"> {{ formData.pay_info.transfer_time }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('transferType')" >
|
||||
<div class="input-width"> {{ formData.pay_info.transfer_type }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('transferMoney')" >
|
||||
<div class="input-width"> {{ formData.pay_info.money }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('transferRemark')" >
|
||||
<div class="input-width"> {{ formData.pay_info.remark }} </div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-if="formData.type == 'refund'">
|
||||
<el-form-item :label="t('outTradeNo')" >
|
||||
<div class="input-width"> {{ formData.pay_info.out_trade_no }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('createTime')" >
|
||||
<div class="input-width"> {{ formData.pay_info.create_time }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('refundMoney')" >
|
||||
<div class="input-width"> {{ formData.pay_info.money }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('failReason')" >
|
||||
<div class="input-width"> {{ formData.pay_info.fail_reason }} </div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-if="formData.type == 'pay'">
|
||||
<el-form-item :label="t('outTradeNo')" >
|
||||
<div class="input-width"> {{ formData.pay_info.out_trade_no }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('createTime')" >
|
||||
<div class="input-width"> {{ formData.pay_info.create_time }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('money')" >
|
||||
<div class="input-width"> {{ formData.pay_info.money }} </div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('body')" >
|
||||
<div class="input-width"> {{ formData.pay_info.body }} </div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<!-- <el-form-item :label="t('headimg')" >
|
||||
<div class="flex items-center">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-if="formData.member.headimg" :src="img(formData.member.headimg)" alt="" >
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-else src="@/assets/images/default_headimg.png" alt="" >
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('memberId')" >
|
||||
<div class="input-width"> {{ formData.member.member_no }} </div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('nickName')" >
|
||||
<div class="input-width"> {{ formData.member.nickname }} </div>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item :label="t('mobile')" >
|
||||
<div class="input-width"> {{ formData.member.mobile }} </div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('accountData')" >
|
||||
<div class="input-width"> {{ formData.account_data }} </div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('fromType')" >
|
||||
<div class="input-width"> {{ formData.from_type_name }} </div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('memo')" >
|
||||
<div class="input-width"> {{ formData.memo }} </div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('createTime')" >
|
||||
<div class="input-width"> {{ formData.create_time }} </div>
|
||||
</el-form-item> -->
|
||||
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="showDialog = false">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getAccountList, getAccountStat, getAccountType } from '@/api/site'
|
||||
import { img } from '@/utils/common'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title;
|
||||
|
||||
let siteAccountLogTable = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam:{
|
||||
site_id: "",
|
||||
type: "",
|
||||
money: "",
|
||||
trade_no: "",
|
||||
create_time: ""
|
||||
}
|
||||
})
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
/**
|
||||
* 获取站点账单记录列表
|
||||
*/
|
||||
const loadSiteAccountLogList = (page: number = 1) => {
|
||||
siteAccountLogTable.loading = true
|
||||
siteAccountLogTable.page = page
|
||||
|
||||
getAccountList({
|
||||
page: siteAccountLogTable.page,
|
||||
limit: siteAccountLogTable.limit,
|
||||
...siteAccountLogTable.searchParam
|
||||
}).then(res => {
|
||||
siteAccountLogTable.loading = false
|
||||
siteAccountLogTable.data = res.data.data
|
||||
siteAccountLogTable.total = res.data.total
|
||||
}).catch(() => {
|
||||
siteAccountLogTable.loading = false
|
||||
})
|
||||
}
|
||||
loadSiteAccountLogList()
|
||||
|
||||
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
loadSiteAccountLogList()
|
||||
}
|
||||
|
||||
const accountType = ref([])
|
||||
const checkAccountType = () => {
|
||||
getAccountType().then(res=>{
|
||||
accountType.value = res.data
|
||||
console.log(accountType.value)
|
||||
})
|
||||
}
|
||||
checkAccountType()
|
||||
const showDialog = ref(false)
|
||||
const formData = ref([]);
|
||||
const detailEvent = (info) => {
|
||||
showDialog.value = true
|
||||
formData.value = info
|
||||
}
|
||||
|
||||
const accountStat = ref([])
|
||||
const checkAccountStat = async () => {
|
||||
accountStat.value = await (await getAccountStat()).data
|
||||
}
|
||||
checkAccountStat()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
File diff suppressed because one or more lines are too long
340
admin/src/views/index/site_index__.vue
Normal file
340
admin/src/views/index/site_index__.vue
Normal file
File diff suppressed because one or more lines are too long
@ -25,6 +25,7 @@ import { setMap, getMap } from '@/api/sys'
|
||||
import { FormInstance, } from 'element-plus'
|
||||
|
||||
const loading = ref(false)
|
||||
const formRef = ref<FormInstance>()
|
||||
const formData = reactive<Record<string, string>>({
|
||||
key : ''
|
||||
})
|
||||
@ -39,7 +40,6 @@ setFormData()
|
||||
*/
|
||||
const save = async (formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
|
||||
setMap(formData).then(() => {
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
|
||||
@ -101,10 +101,10 @@ const loadNoticeList = () => {
|
||||
item.sms_type = item.support_type.indexOf('sms') !== -1 ? 1 : 0;
|
||||
item.wechat_type = item.support_type.indexOf('wechat') !== -1 ? 1 : 0;
|
||||
item.weapp_type = item.support_type.indexOf('weapp') !== -1 ? 1 : 0;
|
||||
if(item.receiver_type == 1){
|
||||
if(item.receiver_type == 0){
|
||||
noticeTableData.buyer.push(item)
|
||||
}
|
||||
if(item.receiver_type == 2){
|
||||
if(item.receiver_type == 1){
|
||||
noticeTableData.seller.push(item)
|
||||
}
|
||||
})
|
||||
|
||||
@ -110,7 +110,7 @@ const setTemplateList = async () => {
|
||||
Object.keys(res.data).forEach(key => {
|
||||
const item = res.data[key]
|
||||
const value = { value: key, name: item.name }
|
||||
item.receiver_type == 1 ? templateList.buyer.list.push(value) : templateList.seller.list.push(value)
|
||||
item.receiver_type == 0 ? templateList.buyer.list.push(value) : templateList.seller.list.push(value)
|
||||
})
|
||||
}).catch(() => {
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ const setTemplateList = async () => {
|
||||
Object.keys(res.data).forEach(key => {
|
||||
const item = res.data[key]
|
||||
const value = { value: key, name: item.name }
|
||||
item.receiver_type == 1 ? templateList.buyer.list.push(value) : templateList.seller.list.push(value)
|
||||
item.receiver_type == 0 ? templateList.buyer.list.push(value) : templateList.seller.list.push(value)
|
||||
})
|
||||
}).catch(() => {
|
||||
|
||||
|
||||
@ -126,20 +126,20 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider content-position="left">{{ t('commentField') }}</el-divider>
|
||||
<div>
|
||||
<div class="flex flex-row ">
|
||||
<div class="text-xs text-gray-500 w-20 items-center justify-end flex">{{ t('isRequired') }}:
|
||||
<div class="text-xs text-gray-500 w-20 items-center justify-end flex">{{ t('viewType') }}:
|
||||
</div>
|
||||
<div>
|
||||
<el-radio-group class="ml-4"
|
||||
v-model="formData.table_column[formData.editFiledIndex]['is_required']">
|
||||
<el-radio :label="1" size="large">{{ t('yes') }}</el-radio>
|
||||
<el-radio :label="0" size="large">{{ t('no') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<el-select class="m-2" :placeholder="t('selectPlaceholder')" size="small"
|
||||
v-model="formData.table_column[formData.editFiledIndex]['view_type']">
|
||||
<el-option :label="item.label" :value="item.value" v-for="(item, index) in viewType"
|
||||
:key="index" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider content-position="left">{{t('addAndEdit')}}</el-divider>
|
||||
<div>
|
||||
<div class="flex flex-row ">
|
||||
<div class="text-xs text-gray-500 w-20 items-center justify-end flex">{{ t('isInsert') }}:
|
||||
</div>
|
||||
@ -162,6 +162,21 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row ">
|
||||
<div class="text-xs text-gray-500 w-20 items-center justify-end flex">{{ t('isRequired') }}:
|
||||
</div>
|
||||
<div>
|
||||
<el-radio-group class="ml-4"
|
||||
v-model="formData.table_column[formData.editFiledIndex]['is_required']">
|
||||
<el-radio :label="1" size="large">{{ t('yes') }}</el-radio>
|
||||
<el-radio :label="0" size="large">{{ t('no') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider content-position="left">{{t('listSearch')}}</el-divider>
|
||||
<div>
|
||||
|
||||
<div class="flex flex-row ">
|
||||
<div class="text-xs text-gray-500 w-20 items-center justify-end flex">{{ t('isLists') }}:</div>
|
||||
<div>
|
||||
@ -204,17 +219,7 @@
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row ">
|
||||
<div class="text-xs text-gray-500 w-20 items-center justify-end flex">{{ t('viewType') }}:
|
||||
</div>
|
||||
<div>
|
||||
<el-select class="m-2" :placeholder="t('selectPlaceholder')" size="small"
|
||||
v-model="formData.table_column[formData.editFiledIndex]['view_type']">
|
||||
<el-option :label="item.label" :value="item.value" v-for="(item, index) in viewType"
|
||||
:key="index" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
|
||||
@ -1,160 +0,0 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-[24px]">{{pageName}}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="cronTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('title')" prop="title">
|
||||
<el-input v-model="cronTableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('type')" prop="type">
|
||||
<el-select v-model="cronTableData.searchParam.type" clearable class="input-width">
|
||||
<el-option :label="t('selectPlaceholder')" value="" />
|
||||
<el-option :label="item" :value="key" v-for="(item, key) in typeList" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('lastTime')" prop="last_time">
|
||||
<el-date-picker
|
||||
v-model="cronTableData.searchParam.last_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="loadCronList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div>
|
||||
<el-table :data="cronTableData.data" size="large" v-loading="cronTableData.loading">
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !cronTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="title" :show-overflow-tooltip="true" :label="t('title')" min-width="150" />
|
||||
<el-table-column prop="type_name" :label="t('typeName')" min-width="120" />
|
||||
|
||||
<el-table-column :label="t('crondType')" min-width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.type == 'crond'">{{ row.crond_length }}{{ row.crond_type_name }}</span>
|
||||
<span v-else>{{ t('cron') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="count" :label="t('count')" min-width="120" />
|
||||
|
||||
<el-table-column :label="t('lastTime')" min-width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.last_time || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('nextTime')" min-width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.next_time || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('operation')" fixed="right" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="infoEvent(row)">{{ t('info') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="cronTableData.page" v-model:page-size="cronTableData.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="cronTableData.total"
|
||||
@size-change="loadCronList()" @current-change="loadCronList" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
<cron-info ref="cronDialog" @complete="loadCronList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getCronList,getCronType } from '@/api/sys'
|
||||
import { img } from '@/utils/common'
|
||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import CronInfo from '@/views/setting/components/cron-info.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title;
|
||||
|
||||
const cronTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
title: '',
|
||||
type:'',
|
||||
last_time:''
|
||||
}
|
||||
})
|
||||
const typeList = ref([])
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
const setTypeList = async () => {
|
||||
typeList.value = await (await getCronType()).data
|
||||
}
|
||||
setTypeList()
|
||||
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined)=>{
|
||||
if (!formEl) return
|
||||
|
||||
formEl.resetFields();
|
||||
loadCronList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务列表
|
||||
*/
|
||||
const loadCronList = (page: number = 1) => {
|
||||
cronTableData.loading = true
|
||||
cronTableData.page = page
|
||||
|
||||
getCronList({
|
||||
page: cronTableData.page,
|
||||
limit: cronTableData.limit,
|
||||
...cronTableData.searchParam
|
||||
}).then(res => {
|
||||
cronTableData.loading = false
|
||||
cronTableData.data = res.data.data
|
||||
cronTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
cronTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadCronList()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const cronDialog: Record<string, any> | null = ref(null)
|
||||
/**
|
||||
* 查看任务
|
||||
* @param data
|
||||
*/
|
||||
const infoEvent = (data: any) => {
|
||||
cronDialog.value.setFormData(data)
|
||||
cronDialog.value.showDialog = true
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
101
admin/src/views/tools/schedule.vue
Normal file
101
admin/src/views/tools/schedule.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-[24px]">{{pageName}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-table :data="cronTableData.data" size="large" v-loading="cronTableData.loading">
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !cronTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="name" :label="t('title')" min-width="150" />
|
||||
<el-table-column prop="key" :label="t('key')" min-width="150" />
|
||||
<el-table-column :label="t('crondType')" min-width="150">
|
||||
<template #default="{ row }">
|
||||
{{ row.crontab_content }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="cronTableData.page" v-model:page-size="cronTableData.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="cronTableData.total"
|
||||
@size-change="loadCronList()" @current-change="loadCronList" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
<cron-info ref="cronDialog" @complete="loadCronList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getCronList,getCronType } from '@/api/sys'
|
||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import CronInfo from '@/views/setting/components/cron-info.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title;
|
||||
|
||||
const cronTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
title: '',
|
||||
type:'',
|
||||
last_time:''
|
||||
}
|
||||
})
|
||||
const typeList = ref([])
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
const setTypeList = async () => {
|
||||
typeList.value = await (await getCronType()).data
|
||||
}
|
||||
setTypeList()
|
||||
|
||||
/**
|
||||
* 获取任务列表
|
||||
*/
|
||||
const loadCronList = (page: number = 1) => {
|
||||
cronTableData.loading = true
|
||||
cronTableData.page = page
|
||||
|
||||
getCronList({
|
||||
page: cronTableData.page,
|
||||
limit: cronTableData.limit,
|
||||
...cronTableData.searchParam
|
||||
}).then(res => {
|
||||
cronTableData.loading = false
|
||||
cronTableData.data = res.data.data
|
||||
cronTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
cronTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadCronList()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const cronDialog: Record<string, any> | null = ref(null)
|
||||
/**
|
||||
* 查看任务
|
||||
* @param data
|
||||
*/
|
||||
const infoEvent = (data: any) => {
|
||||
cronDialog.value.setFormData(data)
|
||||
cronDialog.value.showDialog = true
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
Loading…
x
Reference in New Issue
Block a user