This commit is contained in:
全栈小学生 2023-05-20 18:44:16 +08:00
parent d0850818d2
commit 40ce047612
143 changed files with 6419 additions and 2405 deletions

View File

@ -10,6 +10,7 @@ declare module '@vue/runtime-core' {
Attachment: typeof import('./src/components/upload-attachment/attachment.vue')['default'] Attachment: typeof import('./src/components/upload-attachment/attachment.vue')['default']
DiyLink: typeof import('./src/components/diy-link/index.vue')['default'] DiyLink: typeof import('./src/components/diy-link/index.vue')['default']
Editor: typeof import('./src/components/editor/index.vue')['default'] Editor: typeof import('./src/components/editor/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAside: typeof import('element-plus/es')['ElAside'] ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar'] ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
@ -17,12 +18,14 @@ declare module '@vue/runtime-core' {
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
@ -39,10 +42,12 @@ declare module '@vue/runtime-core' {
ElImage: typeof import('element-plus/es')['ElImage'] ElImage: typeof import('element-plus/es')['ElImage']
ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMain: typeof import('element-plus/es')['ElMain'] ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
ElPagination: typeof import('element-plus/es')['ElPagination'] ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover'] ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadio: typeof import('element-plus/es')['ElRadio']
@ -58,7 +63,9 @@ declare module '@vue/runtime-core' {
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs'] ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
Icon: typeof import('./src/components/icon/index.vue')['default'] Icon: typeof import('./src/components/icon/index.vue')['default']
PopoverInput: typeof import('./src/components/popover-input/index.vue')['default'] PopoverInput: typeof import('./src/components/popover-input/index.vue')['default']

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image" href="/niucloud.ico" /> <link rel="icon" type="image" href="/niucloud.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>管理端</title> <title></title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@ -13,7 +13,8 @@
"@vueuse/core": "^9.12.0", "@vueuse/core": "^9.12.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.3.1", "axios": "^1.4.0",
"crypto-js": "^4.1.1",
"css-color-function": "^1.3.3", "css-color-function": "^1.3.3",
"echarts": "^5.4.1", "echarts": "^5.4.1",
"element-plus": "^2.2.29", "element-plus": "^2.2.29",
@ -25,6 +26,7 @@
"vue": "^3.2.45", "vue": "^3.2.45",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
"vue-web-terminal": "^3.1.7",
"vue3-video-play": "^1.3.1-beta.6" "vue3-video-play": "^1.3.1-beta.6"
}, },
"devDependencies": { "devDependencies": {

35
admin/src/api/addon.ts Normal file
View File

@ -0,0 +1,35 @@
import request from '@/utils/request'
/**
*
* @returns
*/
export function getAddonLocal(params: Record<string, any>) {
return request.get('addon/local', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getAddonDetial(id: number) {
return request.get(`addon/${id}`)
}
/**
*
* @param params
* @returns
*/
export function installAddon(params: Record<string, any>) {
return request.post(`addon/install/${params.addon}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function uninstallAddon(params: Record<string, any>) {
return request.post(`addon/uninstall/${params.addon}`, params, { showErrorMessage: true, showSuccessMessage: true })
}

View File

@ -35,7 +35,7 @@ export function addArticle(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateArticle(params: Record<string, any>) { export function editArticle(params: Record<string, any>) {
return request.put(`article/article/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`article/article/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -91,7 +91,7 @@ export function addArticleCategory(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateArticleCategory(params: Record<string, any>) { export function editArticleCategory(params: Record<string, any>) {
return request.put(`article/category/${params.category_id}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`article/category/${params.category_id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }

View File

@ -5,8 +5,8 @@ import request from '@/utils/request'
* @param params * @param params
* @returns * @returns
*/ */
export function login(params: Record<string, any>) { export function login(params: Record<string, any>, app_type: string) {
return request.get('login', { params, showErrorMessage: true }) return request.get(`login/${app_type}`, { params, showErrorMessage: true })
} }
/** /**
@ -24,3 +24,11 @@ export function getAuthMenus() {
export function getSiteInfo() { export function getSiteInfo() {
return request.get('auth/site') return request.get('auth/site')
} }
/**
*
* @returns
*/
export function getLoginConfig() {
return request.get('login/config')
}

View File

@ -8,7 +8,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getDiyPageList(params: Record<string, any>) { export function getDiyPageList(params: Record<string, any>) {
return request.get(`diy/diy`, {params}) return request.get(`diy/diy`, { params })
} }
/** /**
@ -26,15 +26,15 @@ export function getDiyPageInfo(id: number) {
* @returns * @returns
*/ */
export function addDiyPage(params: Record<string, any>) { export function addDiyPage(params: Record<string, any>) {
return request.post('diy/diy', params, {showErrorMessage: true, showSuccessMessage: true}) return request.post('diy/diy', params, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
* *
* @param params * @param params
*/ */
export function updateDiyPage(params: Record<string, any>) { export function editDiyPage(params: Record<string, any>) {
return request.put(`diy/diy/${params.id}`, params, {showErrorMessage: true, showSuccessMessage: true}) return request.put(`diy/diy/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
@ -42,15 +42,15 @@ export function updateDiyPage(params: Record<string, any>) {
* @param params * @param params
*/ */
export function setUseDiyPage(params: Record<string, any>) { export function setUseDiyPage(params: Record<string, any>) {
return request.put(`diy/use`, params, {showErrorMessage: true, showSuccessMessage: true}) return request.put(`diy/use`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
* *
* @param params * @param params
*/ */
export function updateDiyPageShare(params: Record<string, any>) { export function editDiyPageShare(params: Record<string, any>) {
return request.put(`diy/diy/share`, params, {showErrorMessage: true, showSuccessMessage: true}) return request.put(`diy/diy/share`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
@ -59,28 +59,28 @@ export function updateDiyPageShare(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteDiyPage(id: number) { export function deleteDiyPage(id: number) {
return request.delete(`diy/diy/${id}`, {showErrorMessage: true, showSuccessMessage: true}) return request.delete(`diy/diy/${id}`, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
* *
*/ */
export function initPage(params: Record<string, any>) { 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>) { 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>) { export function getDiyBottom(params: Record<string, any>) {
return request.get(`diy/bottom`, {params}) return request.get(`diy/bottom`, { params })
} }
/** /**
@ -89,14 +89,14 @@ export function getDiyBottom(params: Record<string, any>) {
* @returns * @returns
*/ */
export function setDiyBottom(params: Record<string, any>) { export function setDiyBottom(params: Record<string, any>) {
return request.post('diy/bottom', params, {showErrorMessage: true, showSuccessMessage: true}) return request.post('diy/bottom', params, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
* *
*/ */
export function getDiyPageType(params: Record<string, any>) { export function getDiyPageType(params: Record<string, any>) {
return request.get(`diy/type`, {params}) return request.get(`diy/type`, { params })
} }
/** /**
@ -105,13 +105,21 @@ export function getDiyPageType(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getDiyRouteList(params: Record<string, any>) { export function getDiyRouteList(params: Record<string, any>) {
return request.get(`diy/route`, {params}) return request.get(`diy/route`, { params })
}
/**
*
* @param params
*/
export function getDiyRouteInfo(params: Record<string, any>) {
return request.get(`diy/route/info`, { params });
} }
/** /**
* *
* @param params * @param params
*/ */
export function updateDiyRouteShare(params: Record<string, any>) { export function editDiyRouteShare(params: Record<string, any>) {
return request.put(`diy/route/share`, params, {showErrorMessage: true, showSuccessMessage: true}) return request.put(`diy/route/share`, params, { showErrorMessage: true, showSuccessMessage: true })
} }

View File

@ -9,7 +9,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getMemberList(params: Record<string, any>) { export function getMemberList(params: Record<string, any>) {
return request.get(`member/member`, {params}) return request.get(`member/member`, { params })
} }
/** /**
@ -39,6 +39,15 @@ export function getRegisterType(params: Record<string, any>) {
return request.get(`member/registertype`, params) return request.get(`member/registertype`, params)
} }
/**
*
* @param params
* @returns
*/
export function getRegisterChannelType(params: Record<string, any>) {
return request.get(`member/register/channel`, params)
}
/***************************************************** 会员标签 ****************************************************/ /***************************************************** 会员标签 ****************************************************/
@ -49,7 +58,7 @@ export function getRegisterType(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getMemberLabelList(params: Record<string, any>) { export function getMemberLabelList(params: Record<string, any>) {
return request.get(`member/label`, {params}) return request.get(`member/label`, { params })
} }
/** /**
@ -104,20 +113,13 @@ export function getMemberLabelAll() {
* @param params * @param params
* @returns * @returns
*/ */
export function updateMemberDetail(params: Record<string, any>) { export function editMemberDetail(params: Record<string, any>) {
return request.put(`member/member/modify/${params.member_id}/${params.field}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`member/member/modify/${params.member_id}/${params.field}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/***************************************************** 会员零钱 ****************************************************/ /***************************************************** 会员零钱 ****************************************************/
/**
*
* @param params
* @returns
*/
export function getMoneyList(params: Record<string, any>) {
return request.get(`member/account/money`, {params})
}
/***************************************************** 会员账户 ****************************************************/ /***************************************************** 会员账户 ****************************************************/
@ -146,7 +148,23 @@ export function getPointList(params: Record<string, any>) {
*/ */
export function getBalanceList(params: Record<string, any>) { export function getBalanceList(params: Record<string, any>) {
return request.get(`member/account/balance`, { params }) return request.get(`member/account/balance`, { params })
} }
/**
*
* @param params
* @returns
*/
export function getMoneyList(params: Record<string, any>) {
return request.get(`member/account/money`, { params })
}
/**
*
* @param params
* @returns
*/
export function getCommissionList(params: Record<string, any>) {
return request.get(`member/account/commission`, { params })
}
/** /**
* *
* @param params * @param params
@ -154,7 +172,7 @@ export function getBalanceList(params: Record<string, any>) {
*/ */
export function adjustPoint(params: Record<string, any>) { export function adjustPoint(params: Record<string, any>) {
return request.post(`member/account/point`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.post(`member/account/point`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
* *
* @param params * @param params
@ -162,7 +180,7 @@ export function adjustPoint(params: Record<string, any>) {
*/ */
export function adjustBalance(params: Record<string, any>) { export function adjustBalance(params: Record<string, any>) {
return request.post(`member/account/balance`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.post(`member/account/balance`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/***************************************************** 会员相关设置 ****************************************************/ /***************************************************** 会员相关设置 ****************************************************/
@ -173,7 +191,7 @@ export function adjustBalance(params: Record<string, any>) {
*/ */
export function getLoginConfig(params: Record<string, any>) { export function getLoginConfig(params: Record<string, any>) {
return request.get(`member/config/login`, params) return request.get(`member/config/login`, params)
} }
/** /**
* *
* @param params * @param params
@ -183,21 +201,58 @@ export function setLoginConfig(params: Record<string, any>) {
return request.post(`member/config/login`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.post(`member/config/login`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/**
*
* @param params
* @returns
*/
export function getTransfertype() {
return request.get(`member/cash_out/transfertype`)
}
/**
*
* @param params
* @returns
*/
export function getBalanceSum(params: Record<string, any>) {
return request.get(`member/account/sum_balance`, { params })
}
/**
*
* @param params
* @returns
*/
export function getBalanceStatus() {
return request.get(`member/account/type`)
}
/**
*
*/
export function getAccountType(params: Record<string, any>) {
return request.get(`member/account/change_type/${params.account_type}`)
}
/***************************************************** 会员提现 ****************************************************/
/** /**
* *
* @param params * @param params
* @returns * @returns
*/ */
export function getWithdrawConfig() { export function getWithdrawConfig() {
return request.get(`member/config/withdraw`) return request.get(`member/config/cash_out`)
} }
/** /**
* *
* @param params * @param params
* @returns * @returns
*/ */
export function setWithdrawConfig(params: Record<string, any>) { export function setWithdrawConfig(params: Record<string, any>) {
return request.post(`member/config/withdraw`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.post(`member/config/cash_out`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
@ -206,14 +261,53 @@ export function setWithdrawConfig(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getWithdrawList(params: Record<string, any>) { export function getWithdrawList(params: Record<string, any>) {
return request.get(`member/withdraw`, {params}) return request.get(`member/cash_out`, { params })
} }
/** /**
* *
* @param params
* @returns id
*/
export function getWithdrawDetail(id: number) {
return request.get(`member/cash_out/${id}`, {})
}
/**
*
* @param id
* @param params * @param params
* @returns * @returns
*/ */
export function getTransfertype() { export function memberAudit(params: Record<string, any>) {
return request.get(`member/withdraw/transfertype`) return request.put(`member/cash_out/audit/${params.id}/${params.action}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function memberTransfer(params: Record<string, any>) {
return request.put(`member/cash_out/transfer/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param id
* @param params
* @returns
*/
export function editMemberStatus(params: Record<string, any>) {
return request.put(`member/setstatus/${params.status}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getWithdrawStatusList() {
return request.get(`member/cash_out/status`)
} }

View File

@ -1,75 +0,0 @@
import request from '@/utils/request'
/***************************************************** 消息管理 ****************************************************/
/**
*
* @returns
*/
export function getMessageList() {
return request.get('message/message')
}
/**
*
* @param params
* @returns
*/
export function getMessageInfo(key: string) {
return request.get(`message/message/${key}`)
}
/**
*
* @param params
* @returns
*/
export function getMessageLog(params: any) {
return request.get(`message/log`, { params })
}
/**
*
* @param params
* @returns
*/
export function updateMessageStatus(params: Record<string, any>) {
return request.post(`message/message/updatestatus`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function updateMessage(params: Record<string, any>) {
return request.post(`message/message/update`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getSmsList() {
return request.get('message/message/sms')
}
/**
*
* @param sms_type
* @returns
*/
export function getSmsInfo(sms_type: string) {
return request.get(`message/message/sms/${sms_type}`,)
}
/**
*
* @param sms_type
* @param params
* @returns
*/
export function updateSms(params: Record<string, any>) {
return request.put(`message/message/sms/${params.sms_type}`, params, { showErrorMessage: true, showSuccessMessage: true })
}

85
admin/src/api/notice.ts Normal file
View File

@ -0,0 +1,85 @@
import request from '@/utils/request'
/***************************************************** 消息管理 ****************************************************/
/**
*
* @returns
*/
export function getNoticeList() {
return request.get('notice/notice')
}
/**
*
* @param params
* @returns
*/
export function getNoticeInfo(key: string) {
return request.get(`notice/notice/${key}`)
}
/**
*
* @param params
* @returns
*/
export function getNoticeLog(params: any) {
return request.get(`notice/log`, { params })
}
/**
*
* @param params
* @returns
*/
export function editNoticeStatus(params: Record<string, any>) {
return request.post(`notice/notice/editstatus`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function editNotice(params: Record<string, any>) {
return request.post(`notice/notice/edit`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getSmsList() {
return request.get('notice/notice/sms')
}
/**
*
* @param sms_type
* @returns
*/
export function getSmsInfo(sms_type: string) {
return request.get(`notice/notice/sms/${sms_type}`,)
}
/**
*
* @param sms_type
* @param params
* @returns
*/
export function editSms(params: Record<string, any>) {
return request.put(`notice/notice/sms/${params.sms_type}`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
* @param sms_type
* @param params
* @returns
*/
export function getSmsLog(params: Record<string, any>) {
return request.put(`notice/sms/log`, params)
}

View File

@ -27,3 +27,27 @@ export function getRechargeOrderInfo(order_id: number) {
export function getRechargeOrderStatusList() { export function getRechargeOrderStatusList() {
return request.get(`order/recharge/status`) return request.get(`order/recharge/status`)
} }
/**
* 退
* @returns
*/
export function getRefund(params: Record<string, any>) {
return request.get(`refund/refund`, { params })
}
/**
* 退
* @returns
*/
export function getRefundStatus() {
return request.get(`refund/status`)
}
/**
* 退
* @returns
*/
export function rechargeRefund(id) {
return request.get(`order/recharge/refund/${id}`);
}

View File

@ -4,7 +4,7 @@ import request from '@/utils/request'
* *
* @returns * @returns
*/ */
export function getUserInfo(type:string) { export function getUserInfo(type: string) {
return request.get(`auth/get`) return request.get(`auth/get`)
} }
@ -13,6 +13,6 @@ export function getUserInfo(type:string) {
* @returns * @returns
*/ */
export function setUserInfo(params: Record<string, any>) { export function setUserInfo(params: Record<string, any>) {
return request.put(`auth/update`, params, { showErrorMessage: true, showSuccessMessage: true }); return request.put(`auth/edit`, params, { showErrorMessage: true, showSuccessMessage: true });
} }

View File

@ -10,7 +10,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getSiteList(params: Record<string, any>) { export function getSiteList(params: Record<string, any>) {
return request.get(`site/site`, {params}) return request.get(`site/site`, { params })
} }
/** /**
@ -37,7 +37,7 @@ export function addSite(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateSite(params: Record<string, any>) { export function editSite(params: Record<string, any>) {
return request.put(`site/site/${params.site_id}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`site/site/${params.site_id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -59,7 +59,7 @@ export function getStatusList() {
* @returns * @returns
*/ */
export function getSiteGroupList(params: Record<string, any>) { export function getSiteGroupList(params: Record<string, any>) {
return request.get(`site/group`, {params}) return request.get(`site/group`, { params })
} }
/** /**
@ -86,7 +86,7 @@ export function addSiteGroup(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateSiteGroup(params: Record<string, any>) { export function editSiteGroup(params: Record<string, any>) {
return request.put(`site/group/${params.group_id}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`site/group/${params.group_id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -144,7 +144,7 @@ export function addUser(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateUser(params: Record<string, any>) { export function editUser(params: Record<string, any>) {
return request.put(`site/user/${params.uid}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`site/user/${params.uid}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }

View File

@ -9,6 +9,14 @@ import request from '@/utils/request'
export function getInfo() { export function getInfo() {
return request.get('sys/role') return request.get('sys/role')
} }
/**
*
* @returns
*/
export function getUrl() {
return request.get('sys/url')
}
/***************************************************** 用户组 ****************************************************/ /***************************************************** 用户组 ****************************************************/
/** /**
@ -43,7 +51,7 @@ export function addRole(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateRole(params: Record<string, any>) { export function editRole(params: Record<string, any>) {
return request.put(`sys/role/${params.role_id}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`sys/role/${params.role_id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -70,7 +78,7 @@ export function allRole() {
* *
* @returns * @returns
*/ */
export function getMenus(type:string) { export function getMenus(type: string) {
return request.get(`sys/menu/${type}`) return request.get(`sys/menu/${type}`)
} }
@ -98,7 +106,7 @@ export function addMenu(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateMenu(params: Record<string, any>) { export function editMenu(params: Record<string, any>) {
return request.put(`sys/menu/${params.menu_key}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`sys/menu/${params.menu_key}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -180,7 +188,7 @@ export function addAttachmentCategory(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateAttachmentCategory(params: Record<string, any>) { export function editAttachmentCategory(params: Record<string, any>) {
return request.put(`sys/attachment/category/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`sys/attachment/category/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -208,7 +216,7 @@ export function getAttachmentList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteAttachment(params: Record<string, any>) { export function deleteAttachment(params: Record<string, any>) {
return request.delete(`sys/attachment/del`, { data: params, showErrorMessage: true, showSuccessMessage: true}) return request.delete(`sys/attachment/del`, { data: params, showErrorMessage: true, showSuccessMessage: true })
} }
/** /**
@ -266,7 +274,7 @@ export function getStorageInfo(type: string) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateStorage(params: Record<string, any>) { export function editStorage(params: Record<string, any>) {
return request.put(`sys/storage/${params.storage_type}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`sys/storage/${params.storage_type}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -276,7 +284,7 @@ export function updateStorage(params: Record<string, any>) {
* *
* @returns * @returns
*/ */
export function getPayConfig(type:string) { export function getPayConfig(type: string) {
return request.get(`pay/config/${type}`) return request.get(`pay/config/${type}`)
} }
@ -296,13 +304,31 @@ export function getPayList() {
return request.get(`pay/lists`) return request.get(`pay/lists`)
} }
/***************************************************** 打款设置 ****************************************************/
/**
*
* @returns channel
* @returns
*/
export function getTransferInfo(channel) {
return request.get(`pay/channel/lists/${channel}`)
}
/**
*
* @param params
* @returns
*/
export function setTransferInfo(params: Record<string, any>) {
return request.post(`pay/channel/set/transfer`, params)
}
/***************************************************** 定时任务 ****************************************************/ /***************************************************** 定时任务 ****************************************************/
/** /**
* *
* @returns * @returns
*/ */
export function getCronList(params:any) { export function getCronList(params: any) {
return request.get(`sys/cron`, { params }) return request.get(`sys/cron`, { params })
} }
@ -310,7 +336,7 @@ export function getCronList(params:any) {
* *
* @returns * @returns
*/ */
export function getCronInfo(id:string) { export function getCronInfo(id: string) {
return request.get(`sys/cron/${id}`); return request.get(`sys/cron/${id}`);
} }
@ -336,7 +362,7 @@ export function getAgreementList() {
* *
* @returns * @returns
*/ */
export function getAgreementInfo(key:string) { export function getAgreementInfo(key: string) {
return request.get(`sys/agreement/${key}`); return request.get(`sys/agreement/${key}`);
} }
@ -344,7 +370,7 @@ export function getAgreementInfo(key:string) {
* *
* @returns * @returns
*/ */
export function updateAgreement(params: Record<string, any>) { export function editAgreement(params: Record<string, any>) {
return request.put(`sys/agreement/${params.key}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`sys/agreement/${params.key}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -365,3 +391,54 @@ export function getSceneDomain() {
return request.get(`sys/scene_domain`); return request.get(`sys/scene_domain`);
} }
/***************************************************** 登录注册配置 ****************************************************/
/**
*
* @param params
* @returns
*/
export function getConfigLogin() {
return request.get(`sys/config/login`)
}
/**
*
* @param params
* @returns
*/
export function setConfigLogin(params: Record<string, any>) {
return request.put(`sys/config/login`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
*
*/
export function getPayConfigList() {
return request.get(`pay/channel/lists`)
}
/**
*
*/
export function setPatConfig(params: Record<string, any>) {
return request.post(`pay/channel/set/all`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/***************************************************** 刷新菜单 ****************************************************/
/**
*
*/
export function menuRefresh(params: Record<string, any>) {
return request.post(`sys/menu/refresh`,{},{ showErrorMessage: true, showSuccessMessage: true })
}
/***************************************************** 获取应用 ****************************************************/
/**
*
*/
export function getAppMange() {
return request.get(`sys/applist`)
}

View File

@ -8,7 +8,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getGenerateTableList(params: Record<string, any>) { export function getGenerateTableList(params: Record<string, any>) {
return request.get(`generator/generator`, {params}) return request.get(`generator/generator`, { params })
} }
/** /**
@ -35,7 +35,7 @@ export function addGenerateTable(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateGenerateTable(params: Record<string, any>) { export function editGenerateTable(params: Record<string, any>) {
return request.put(`generator/generator/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`generator/generator/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -66,3 +66,11 @@ export function generateTable() {
return request.get(`generator/table`) return request.get(`generator/table`)
} }
/**
*
* @param file
* @returns
*/
export function getSystem() {
return request.get(`sys/system`)
}

View File

@ -37,7 +37,7 @@ export function addUser(params: Record<string, any>) {
* @param params * @param params
* @returns * @returns
*/ */
export function updateUser(params: Record<string, any>) { export function editUser(params: Record<string, any>) {
return request.put(`user/user/${params.uid}`, params, { showErrorMessage: true, showSuccessMessage: true }) return request.put(`user/user/${params.uid}`, params, { showErrorMessage: true, showSuccessMessage: true })
} }

View File

@ -22,7 +22,7 @@ export function getWechatStatic() {
* @param params * @param params
* @returns * @returns
*/ */
export function updateWechatConfig(params: Record<string, any>) { export function editWechatConfig(params: Record<string, any>) {
return request.put('wechat/config', params, { showErrorMessage: true, showSuccessMessage: true }) return request.put('wechat/config', params, { showErrorMessage: true, showSuccessMessage: true })
} }
@ -39,7 +39,7 @@ export function getWechatMenu() {
* @param params * @param params
* @returns * @returns
*/ */
export function updateWechatMenu(params: Record<string, any>) { export function editWechatMenu(params: Record<string, any>) {
return request.put('wechat/menu', params, { showErrorMessage: true, showSuccessMessage: true }) return request.put('wechat/menu', params, { showErrorMessage: true, showSuccessMessage: true })
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -76,6 +76,7 @@
import {cloneDeep} from 'lodash-es' import {cloneDeep} from 'lodash-es'
import {getLink} from '@/api/diy'; import {getLink} from '@/api/diy';
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import { CollectionTag } from '@element-plus/icons-vue';
const prop = defineProps({ const prop = defineProps({
modelValue: { modelValue: {
@ -115,8 +116,8 @@
} }
getLink({}).then((res: any) => { getLink({}).then((res: any) => {
if (res.code == 200) {
link.value = res.data; link.value = res.data;
childList.value = link.value[0].child_list; childList.value = link.value[0].child_list;
if (value.value.name != '') { if (value.value.name != '') {
selectLink.value = cloneDeep(value.value); selectLink.value = cloneDeep(value.value);
@ -126,7 +127,6 @@
}; };
} }
parentLinkName.value = selectLink.value.parent; parentLinkName.value = selectLink.value.parent;
}
}); });
// //

View File

@ -37,8 +37,8 @@
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
let type = ref('element') const type = ref('element')
let visible = ref('false') const visible = ref('false')
// element // element
const element = computed(() => { const element = computed(() => {
@ -47,8 +47,8 @@ const element = computed(() => {
// iconfont // iconfont
const iconfont = computed(() => { const iconfont = computed(() => {
let iconfile = import.meta.globEager('@/styles/iconfont.css')['/src/styles/iconfont.css'].default const iconfile = import.meta.globEager('@/styles/icon/iconfont.css')['/src/styles/iconfont.css'].default
let icons = Array.from(iconfile.matchAll(/(icon.*)\:before/g)) const icons = Array.from(iconfile.matchAll(/(icon.*)\:before/g))
return icons.map(item => { return icons.map(item => {
return item[1] return item[1]

View File

@ -38,9 +38,15 @@
<div class="attachment-list-wrap flex flex-col p-[15px] flex-1"> <div class="attachment-list-wrap flex flex-col p-[15px] flex-1">
<el-row :gutter="15" class="h-[32px]"> <el-row :gutter="15" class="h-[32px]">
<el-col :span="12"> <el-col :span="12">
<div class="flex">
<el-upload v-bind="upload" ref="uploadRef"> <el-upload v-bind="upload" ref="uploadRef">
<el-button type="primary">{{ t('upload.upload' + type) }}</el-button> <el-button type="primary">{{ t('upload.upload' + type) }}</el-button>
</el-upload> </el-upload>
<el-button v-if="operate === false" class="ml-[10px]" type="primary" @click="operate = true">{{
t('edit') }}</el-button>
<el-button v-else class="ml-[10px]" type="primary" @click="operate = false">{{ t('complete')
}}</el-button>
</div>
</el-col> </el-col>
<el-col :span="12" class="text-right"> <el-col :span="12" class="text-right">
<el-input v-model="attachmentParam.real_name" class="m-0 w-[200px]" <el-input v-model="attachmentParam.real_name" class="m-0 w-[200px]"
@ -50,7 +56,7 @@
</el-row> </el-row>
<div class="flex-1 my-[15px] h-0" v-loading="attachment.loading"> <div class="flex-1 my-[15px] h-0" v-loading="attachment.loading">
<el-scrollbar> <el-scrollbar>
<div class="flex flex-wrap" v-if="attachment.data.length"> <div class="flex flex-wrap" v-if="attachment.data.length && operate === true">
<div class="attachment-item mr-[10px]" :class="scene == 'select' ? 'w-[100px]' : 'w-[120px]'" <div class="attachment-item mr-[10px]" :class="scene == 'select' ? 'w-[100px]' : 'w-[120px]'"
v-for="(item, index) in attachment.data" :key="index"> v-for="(item, index) in attachment.data" :key="index">
<div class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center" <div class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center"
@ -94,13 +100,32 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flex flex-wrap" v-else-if="attachment.data.length && operate === false">
<div class="attachment-item mr-[10px] w-[120px]" v-for="(item, index) in attachment.data"
:key="index">
<div
class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
<el-image :src="img(item.url)" fit="contain" v-if="type == 'image'"
:preview-src-list="item.image_list"></el-image>
<video :src="img(item.url)" v-else></video>
</div>
<div class="flex items-center">
<el-tooltip placement="top">
<template #content>{{ item.real_name }}</template>
<div class="truncate my-[10px] cursor-pointer text-base flex-1 ">
{{ item.real_name }}
</div>
</el-tooltip>
</div>
</div>
</div>
<div class="flex items-center justify-center" v-else> <div class="flex items-center justify-center" v-else>
<el-empty :description="t('upload.attachmentEmpty')" :image-size="100" /> <el-empty v-if="!attachment.loading" :description="t('upload.attachmentEmpty')" :image-size="100" />
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8" v-if="scene == 'attachment'"> <el-col :span="8" v-if="scene == 'attachment' && operate === true">
<div class="flex items-center"> <div class="flex items-center">
<el-checkbox v-model="selectAll" :label="t('selectAll')" size="large" /> <el-checkbox v-model="selectAll" :label="t('selectAll')" size="large" />
<el-button class="ml-[15px]" :disabled="batchOperateDisabled" @click="deleteAttachmentEvent()">{{ <el-button class="ml-[15px]" :disabled="batchOperateDisabled" @click="deleteAttachmentEvent()">{{
@ -163,7 +188,7 @@ import {
getAttachmentCategoryList as attachmentCategoryList, getAttachmentCategoryList as attachmentCategoryList,
getAttachmentList as attachmentList, getAttachmentList as attachmentList,
addAttachmentCategory as addCategory, addAttachmentCategory as addCategory,
updateAttachmentCategory as updateCategory, editAttachmentCategory as updateCategory,
deleteAttachmentCategory as deleteCategory, deleteAttachmentCategory as deleteCategory,
deleteAttachment, deleteAttachment,
moveAttachment moveAttachment
@ -172,6 +197,7 @@ import { debounce, img, getToken } from '@/utils/common'
import { ElMessage, UploadFile, UploadFiles, ElMessageBox } from 'element-plus' import { ElMessage, UploadFile, UploadFiles, ElMessageBox } from 'element-plus'
import storage from '@/utils/storage' import storage from '@/utils/storage'
const operate = ref(false)
const prop = defineProps({ const prop = defineProps({
// //
limit: { limit: {
@ -196,7 +222,7 @@ const attachmentCategory: Record<string, any> = reactive({
data: [] data: []
}) })
const attachment: Record<string, any> = reactive({ const attachment: Record<string, any> = reactive({
loading: false, loading: true,
page: 1, page: 1,
total: 0, total: 0,
limit: prop.scene == 'select' ? 10 : 20, limit: prop.scene == 'select' ? 10 : 20,
@ -241,6 +267,13 @@ const getAttachmentList = debounce((page: number = 1) => {
attachment.total = res.data.total attachment.total = res.data.total
attachment.loading = false attachment.loading = false
prop.scene == 'attachment' && clearSelected() prop.scene == 'attachment' && clearSelected()
for (let i = 0; i < attachment.data.length; i++) {
attachment.data[i]['image_list'] = []
attachment.data[i]['image_list'].push(img(res.data.data[i]['url']))
}
}).catch(() => { }).catch(() => {
attachment.loading = false attachment.loading = false
}) })
@ -508,5 +541,4 @@ defineExpose({
background: #fff !important; background: #fff !important;
box-shadow: var(--el-box-shadow-light); box-shadow: var(--el-box-shadow-light);
} }
} }</style>
</style>

View File

@ -22,7 +22,7 @@ const prop = defineProps({
}, },
api: { api: {
type: String, type: String,
default: 'sys/document' default: 'sys/document/document'
} }
}) })
@ -41,7 +41,7 @@ const upload: Record<string, any> = {
action: `${import.meta.env.VITE_APP_BASE_URL}/${prop.api}`, action: `${import.meta.env.VITE_APP_BASE_URL}/${prop.api}`,
showFileList: false, showFileList: false,
headers: {}, headers: {},
accept: '.doc,.docx,.xml,.txt,.pem,.zip,.rar,.7z', accept: '.doc,.docx,.xml,.txt,.pem,.zip,.rar,.7z,.crt',
onSuccess: (response: any, uploadFile: UploadFile) => { onSuccess: (response: any, uploadFile: UploadFile) => {
value.value = response.data.url value.value = response.data.url
ElMessage({ ElMessage({

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,260 @@
<template>
<div style="position: relative"
>
<div class="verify-img-out">
<div class="verify-img-panel" :style="{'width': setSize.imgWidth,
'height': setSize.imgHeight,
'background-size' : setSize.imgWidth + ' '+ setSize.imgHeight,
'margin-bottom': vSpace + 'px'}"
>
<div class="verify-refresh" style="z-index:3" @click="refresh" v-show="showRefresh">
<i class="iconfont icon-refresh"></i>
</div>
<img :src="'data:image/png;base64,'+pointBackImgBase"
ref="canvas"
alt="" style="width:100%;height:100%;display:block"
@click="bindingClick?canvasClick($event):undefined">
<div v-for="(tempPoint, index) in tempPoints" :key="index" class="point-area"
:style="{
'background-color':'#1abd6c',
color:'#fff',
'z-index':9999,
width:'20px',
height:'20px',
'text-align':'center',
'line-height':'20px',
'border-radius': '50%',
position:'absolute',
top:parseInt(tempPoint.y-10) + 'px',
left:parseInt(tempPoint.x-10) + 'px'
}">
{{index + 1}}
</div>
</div>
</div>
<!-- 'height': this.barSize.height, -->
<div class="verify-bar-area"
:style="{'width': setSize.imgWidth,
'color': this.barAreaColor,
'border-color': this.barAreaBorderColor,
'line-height':this.barSize.height}">
<span class="verify-msg">{{text}}</span>
</div>
</div>
</template>
<script type="text/babel">
/**
* VerifyPoints
* @description 点选
* */
import {resetSize, _code_chars, _code_color1, _code_color2} from './../utils/util'
import {aesEncrypt} from "./../utils/ase"
import {reqGet,reqCheck} from "./../api/index"
import { computed, onMounted, reactive, ref,watch,nextTick,toRefs, watchEffect,getCurrentInstance} from 'vue';
export default {
name: 'VerifyPoints',
props: {
//popfixed
mode: {
type: String,
default: 'fixed'
},
captchaType:{
type:String,
},
//
vSpace: {
type: Number,
default: 5
},
imgSize: {
type: Object,
default() {
return {
width: '310px',
height: '155px'
}
}
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '40px'
}
}
}
},
setup(props,context){
const {mode,captchaType,vSpace,imgSize,barSize} = toRefs(props)
const { proxy } = getCurrentInstance();
let secretKey = ref(''), //ase
checkNum = ref(3), //
fontPos = reactive([]), //
checkPosArr = reactive([]), //
num = ref(1), //
pointBackImgBase = ref(''), //
poinTextList = reactive([]), //
backToken = ref(''), //token
setSize = reactive({
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0
}),
tempPoints = reactive([]),
text = ref(''),
barAreaColor = ref(undefined),
barAreaBorderColor = ref(undefined),
showRefresh = ref(true),
bindingClick = ref(true)
const init = ()=>{
//
fontPos.splice(0, fontPos.length)
checkPosArr.splice(0, checkPosArr.length)
num.value = 1
getPictrue();
nextTick(() => {
let {imgHeight,imgWidth,barHeight,barWidth} = resetSize(proxy)
setSize.imgHeight = imgHeight
setSize.imgWidth = imgWidth
setSize.barHeight = barHeight
setSize.barWidth = barWidth
proxy.$parent.$emit('ready', proxy)
})
}
onMounted(()=>{
//
init()
proxy.$el.onselectstart = function () {
return false
}
})
const canvas = ref(null)
const canvasClick = (e)=>{
checkPosArr.push(getMousePos(canvas, e));
if (num.value == checkNum.value) {
num.value = createPoint(getMousePos(canvas, e));
//
let arr = pointTransfrom(checkPosArr,setSize)
checkPosArr.length = 0
checkPosArr.push(...arr);
//
setTimeout(() => {
// var flag = this.comparePos(this.fontPos, this.checkPosArr);
//
var captchaVerification = secretKey.value? aesEncrypt(backToken.value+'---'+JSON.stringify(checkPosArr),secretKey.value):backToken.value+'---'+JSON.stringify(checkPosArr)
let data = {
captchaType:captchaType.value,
"captcha_code":secretKey.value? aesEncrypt(JSON.stringify(checkPosArr),secretKey.value):JSON.stringify(checkPosArr),
"captcha_key":backToken.value
}
reqCheck(data).then(res=>{
if (res.code == "200") {
barAreaColor.value = '#4cae4c'
barAreaBorderColor.value = '#5cb85c'
text.value = '验证成功'
bindingClick.value = false
if (mode.value=='pop') {
setTimeout(()=>{
proxy.$parent.clickShow = false;
refresh();
},1500)
}
proxy.$parent.$emit('success', {captchaVerification})
}else{
proxy.$parent.$emit('error', proxy)
barAreaColor.value = '#d9534f'
barAreaBorderColor.value = '#d9534f'
text.value = '验证失败'
setTimeout(() => {
refresh();
}, 700);
}
})
}, 400);
}
if (num.value < checkNum.value) {
num.value = createPoint(getMousePos(canvas, e));
}
}
//
const getMousePos = function (obj, e) {
var x = e.offsetX
var y = e.offsetY
return {x, y}
}
//
const createPoint = function (pos) {
tempPoints.push(Object.assign({}, pos))
return num.value+1;
}
const refresh = function () {
tempPoints.splice(0, tempPoints.length)
barAreaColor.value = '#000'
barAreaBorderColor.value = '#ddd'
bindingClick.value = true
fontPos.splice(0, fontPos.length)
checkPosArr.splice(0, checkPosArr.length)
num.value = 1
getPictrue();
text.value = '验证失败'
showRefresh.value = true
}
//
function getPictrue() {
let data = {
captchaType:captchaType.value
}
reqGet(data).then(res=>{
if (res.code == "200") {
pointBackImgBase.value = res.data.originalImageBase64
backToken.value = res.data.token
secretKey.value = res.data.secretKey
poinTextList.value = res.data.wordList
text.value = '请依次点击【' + poinTextList.value.join(",") + '】'
}else{
text.value = res.msg;
}
})
}
//
const pointTransfrom = function(pointArr,imgSize){
var newPointArr = pointArr.map(p=>{
let x = Math.round(310 * p.x/parseInt(imgSize.imgWidth))
let y =Math.round(155 * p.y/parseInt(imgSize.imgHeight))
return {x,y}
})
return newPointArr
}
return {
secretKey,
checkNum,
fontPos,
checkPosArr,
num,
pointBackImgBase,
poinTextList,
backToken,
setSize,
tempPoints,
text,
barAreaColor,
barAreaBorderColor,
showRefresh,
bindingClick,
init,
canvas,
canvasClick,
getMousePos,createPoint,refresh,getPictrue,pointTransfrom
}
},
}
</script>

View File

@ -0,0 +1,366 @@
<template>
<div style="position: relative;">
<div v-if="type === '2'" class="verify-img-out"
:style="{height: (parseInt(setSize.imgHeight) + vSpace) + 'px'}"
>
<div class="verify-img-panel" :style="{width: setSize.imgWidth,
height: setSize.imgHeight,}">
<img :src="'data:image/png;base64,'+backImgBase" alt="" style="width:100%;height:100%;display:block">
<div class="verify-refresh" @click="refresh" v-show="showRefresh"><i class="iconfont icon-refresh"></i>
</div>
<transition name="tips">
<span class="verify-tips" v-if="tipWords" :class="passFlag ?'suc-bg':'err-bg'">{{tipWords}}</span>
</transition>
</div>
</div>
<!-- 公共部分 -->
<div class="verify-bar-area" :style="{width: setSize.imgWidth,
height: barSize.height,
'line-height':barSize.height}">
<span class="verify-msg" v-text="text"></span>
<div class="verify-left-bar"
:style="{width: (leftBarWidth!==undefined)?leftBarWidth: barSize.height, height: barSize.height, 'border-color': leftBarBorderColor, transaction: transitionWidth}">
<span class="verify-msg" v-text="finishText"></span>
<div class="verify-move-block"
@touchstart="start"
@mousedown="start"
:style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}">
<i :class="['verify-icon iconfont', iconClass]"
:style="{color: iconColor}"></i>
<div v-if="type === '2'" class="verify-sub-block"
:style="{'width':Math.floor(parseInt(setSize.imgWidth)*47/310)+ 'px',
'height': setSize.imgHeight,
'top':'-' + (parseInt(setSize.imgHeight) + vSpace) + 'px',
'background-size': setSize.imgWidth + ' ' + setSize.imgHeight,
}">
<img :src="'data:image/png;base64,'+blockBackImgBase" alt="" style="width:100%;height:100%;display:block;-webkit-user-drag:none;">
</div>
</div>
</div>
</div>
</div>
</template>
<script type="text/babel">
/**
* VerifySlide
* @description 滑块
* */
import {aesEncrypt} from "./../utils/ase"
import {resetSize} from './../utils/util'
import {reqGet,reqCheck} from "./../api/index"
import { computed, onMounted, reactive, ref,watch,nextTick,toRefs, watchEffect,getCurrentInstance} from 'vue';
// "captchaType":"blockPuzzle",
export default {
name: 'VerifySlide',
props: {
captchaType:{
type:String,
},
type: {
type: String,
default: '1'
},
//popfixed
mode: {
type: String,
default: 'fixed'
},
vSpace: {
type: Number,
default: 5
},
explain: {
type: String,
default: '向右滑动完成验证'
},
imgSize: {
type: Object,
default() {
return {
width: '310px',
height: '155px'
}
}
},
blockSize: {
type: Object,
default() {
return {
width: '50px',
height: '50px'
}
}
},
barSize: {
type: Object,
default() {
return {
width: '310px',
height: '40px'
}
}
}
},
setup(props,context){
const {mode,captchaType,vSpace,imgSize,barSize,type,blockSize,explain} = toRefs(props)
const { proxy } = getCurrentInstance();
let secretKey = ref(''), //ase
passFlag = ref(''), //
backImgBase = ref(''), //
blockBackImgBase = ref(''), //
backToken = ref(''), //token
startMoveTime = ref(''), //
endMovetime = ref(''), //
tipsBackColor = ref(''), //
tipWords = ref(''),
text = ref(''),
finishText = ref(''),
setSize = reactive({
imgHeight: 0,
imgWidth: 0,
barHeight: 0,
barWidth: 0
}),
top = ref(0),
left = ref(0),
moveBlockLeft = ref(undefined),
leftBarWidth = ref(undefined),
//
moveBlockBackgroundColor = ref(undefined),
leftBarBorderColor = ref('#ddd'),
iconColor = ref(undefined),
iconClass = ref('icon-right'),
status = ref(false), //
isEnd = ref(false) , //
showRefresh = ref(true),
transitionLeft = ref(''),
transitionWidth = ref(''),
startLeft = ref(0)
const barArea = computed(()=>{
return proxy.$el.querySelector('.verify-bar-area')
})
function init() {
text.value = explain.value
getPictrue();
nextTick(() => {
let {imgHeight,imgWidth,barHeight,barWidth} = resetSize(proxy)
setSize.imgHeight = imgHeight
setSize.imgWidth = imgWidth
setSize.barHeight = barHeight
setSize.barWidth = barWidth
proxy.$parent.$emit('ready', proxy)
})
window.removeEventListener("touchmove", function (e) {
move(e);
});
window.removeEventListener("mousemove", function (e) {
move(e);
});
//
window.removeEventListener("touchend", function () {
end();
});
window.removeEventListener("mouseup", function () {
end();
});
window.addEventListener("touchmove", function (e) {
move(e);
});
window.addEventListener("mousemove", function (e) {
move(e);
});
//
window.addEventListener("touchend", function () {
end();
});
window.addEventListener("mouseup", function () {
end();
});
}
watch(type,()=>{
init()
})
onMounted(()=>{
//
init()
proxy.$el.onselectstart = function () {
return false
}
})
//
function start(e) {
e = e || window.event
if (!e.touches) { //PC
var x = e.clientX;
} else { //
var x = e.touches[0].pageX;
}
console.log(barArea);
startLeft.value =Math.floor(x - barArea.value.getBoundingClientRect().left);
startMoveTime.value = +new Date(); //
if (isEnd.value == false) {
text.value = ''
moveBlockBackgroundColor.value = '#337ab7'
leftBarBorderColor.value = '#337AB7'
iconColor.value = '#fff'
e.stopPropagation();
status.value = true;
}
}
//
function move(e) {
e = e || window.event
if (status.value && isEnd.value == false) {
if (!e.touches) { //PC
var x = e.clientX;
} else { //
var x = e.touches[0].pageX;
}
var bar_area_left = barArea.value.getBoundingClientRect().left;
var move_block_left = x - bar_area_left //left
if (move_block_left >= barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2) {
move_block_left = barArea.value.offsetWidth - parseInt(parseInt(blockSize.value.width) / 2) - 2;
}
if (move_block_left <= 0) {
move_block_left = parseInt(parseInt(blockSize.value.width) / 2);
}
//left
moveBlockLeft.value = (move_block_left - startLeft.value) + "px"
leftBarWidth.value = (move_block_left - startLeft.value) + "px"
}
}
//
function end() {
endMovetime.value = +new Date();
//
if (status.value && isEnd.value == false) {
var moveLeftDistance = parseInt((moveBlockLeft.value || '').replace('px', ''));
moveLeftDistance = moveLeftDistance * 310/ parseInt(setSize.imgWidth)
let data = {
captchaType:captchaType.value,
"captcha_code":secretKey.value ? aesEncrypt(JSON.stringify({x:moveLeftDistance,y:5.0}),secretKey.value):JSON.stringify({x:moveLeftDistance,y:5.0}),
"captcha_key":backToken.value
}
reqCheck(data).then(res=>{
if (res.code == "200") {
moveBlockBackgroundColor.value = '#5cb85c'
leftBarBorderColor.value = '#5cb85c'
iconColor.value = '#fff'
iconClass.value = 'icon-check'
showRefresh.value = false
isEnd.value = true;
if (mode.value=='pop') {
setTimeout(()=>{
proxy.$parent.clickShow = false;
refresh();
},1500)
}
passFlag.value = true
tipWords.value = `${((endMovetime.value-startMoveTime.value)/1000).toFixed(2)}s验证成功`
var captchaVerification = secretKey.value ? aesEncrypt(backToken.value+'---'+JSON.stringify({x:moveLeftDistance,y:5.0}),secretKey.value):backToken.value+'---'+JSON.stringify({x:moveLeftDistance,y:5.0})
setTimeout(()=>{
tipWords.value = ""
proxy.$parent.closeBox();
proxy.$parent.$emit('success', {captchaVerification})
},1000)
}else{
moveBlockBackgroundColor.value = '#d9534f'
leftBarBorderColor.value = '#d9534f'
iconColor.value = '#fff'
iconClass.value = 'icon-close'
passFlag.value = false
setTimeout(function () {
refresh();
}, 1000);
proxy.$parent.$emit('error',proxy)
tipWords.value = "验证失败"
setTimeout(()=>{
tipWords.value = ""
},1000)
}
})
status.value = false;
}
}
const refresh = ()=>{
showRefresh.value = true
finishText.value = ''
transitionLeft.value = 'left .3s'
moveBlockLeft.value = 0
leftBarWidth.value = undefined
transitionWidth.value = 'width .3s'
leftBarBorderColor.value = '#ddd'
moveBlockBackgroundColor.value = '#fff'
iconColor.value = '#000'
iconClass.value = 'icon-right'
isEnd.value = false
getPictrue()
setTimeout(() => {
transitionWidth.value = ''
transitionLeft.value = ''
text.value = explain.value
}, 300)
}
//
function getPictrue(){
let data = {
captchaType:captchaType.value
}
reqGet(data).then(res=>{
if (res.code == "200") {
backImgBase.value = res.data.originalImageBase64
blockBackImgBase.value = res.data.jigsawImageBase64
backToken.value = res.data.token
secretKey.value = res.data.secretKey
}else{
tipWords.value = res.msg;
}
})
}
return {
secretKey, //ase
passFlag, //
backImgBase, //
blockBackImgBase, //
backToken, //token
startMoveTime, //
endMovetime, //
tipsBackColor, //
tipWords,
text,
finishText,
setSize,
top,
left,
moveBlockLeft,
leftBarWidth,
//
moveBlockBackgroundColor,
leftBarBorderColor,
iconColor,
iconClass,
status, //
isEnd, //
showRefresh,
transitionLeft,
transitionWidth,
barArea,
refresh,
start
}
},
}
</script>

View File

@ -0,0 +1,31 @@
/**
* 此处可直接引用自己项目封装好的 axios 配合后端联调
*/
import request from "./../utils/axios" //组件内部封装的axios
// import request from "@/api/axios.js" //调用项目封装的axios
//获取验证图片 以及token
export function reqGet(data) {
return request.get('/captcha/create',{params:{...data}});
// return request({
// url: '/captcha/create',
// method: 'get',
// data
// })
}
//滑动或者点选验证
export function reqCheck(data) {
return request.get('/captcha/check',{params:{...data}});
// return request({
// url: '/captcha/check',
// method: 'post',
// data
// })
}

View File

@ -0,0 +1,11 @@
import CryptoJS from 'crypto-js'
/**
* @word 要加密的内容
* @keyWord String 服务器随机返回的关键字
* */
export function aesEncrypt(word,keyWord="XwKsGlMcdPMEhR1B"){
var key = CryptoJS.enc.Utf8.parse(keyWord);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return encrypted.toString();
}

View File

@ -0,0 +1,30 @@
import axios from 'axios';
axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_URL;
const service = axios.create({
timeout: 40000,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json; charset=UTF-8'
},
})
service.interceptors.request.use(
config => {
return config
},
error => {
Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
response => {
const res = response.data;
return res
},
error => {
}
)
export default service

View File

@ -0,0 +1,35 @@
export function resetSize(vm) {
var img_width, img_height, bar_width, bar_height; //图片的宽度、高度,移动条的宽度、高度
var parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth
var parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight
if (vm.imgSize.width.indexOf('%') != -1) {
img_width = parseInt(vm.imgSize.width) / 100 * parentWidth + 'px'
} else {
img_width = vm.imgSize.width;
}
if (vm.imgSize.height.indexOf('%') != -1) {
img_height = parseInt(vm.imgSize.height) / 100 * parentHeight + 'px'
} else {
img_height = vm.imgSize.height
}
if (vm.barSize.width.indexOf('%') != -1) {
bar_width = parseInt(vm.barSize.width) / 100 * parentWidth + 'px'
} else {
bar_width = vm.barSize.width
}
if (vm.barSize.height.indexOf('%') != -1) {
bar_height = parseInt(vm.barSize.height) / 100 * parentHeight + 'px'
} else {
bar_height = vm.barSize.height
}
return {imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height}
}
export const _code_chars = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']
export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC']

View File

@ -9,6 +9,7 @@ let i18n = createI18n({
datetimeFormats: {}, datetimeFormats: {},
numberFormats: {}, numberFormats: {},
globalInjection: true, //是否全局注入 globalInjection: true, //是否全局注入
silentTranslationWarn: true,
messages: { messages: {
"zh-cn": zhCn, "zh-cn": zhCn,
en en

View File

@ -3,7 +3,7 @@ import useAppStore from '@/stores/modules/app'
const t = (message: string) => { const t = (message: string) => {
const path = useAppStore().route const path = useAppStore().route
const file = path == '/' ? 'index' : path.replace('/', '').replaceAll('/', '.') const file = path == '/' ? 'index' : path.replace(/^(\/admin\/|\/site\/|\/)/, '').replaceAll('/', '.')
const key = `${file}.${message}` const key = `${file}.${message}`
return i18n.global.t(key) != key ? i18n.global.t(key) : i18n.global.t(message) return i18n.global.t(key) != key ? i18n.global.t(key) : i18n.global.t(message)
} }

View File

@ -30,12 +30,7 @@ class Language {
*/ */
public async loadLocaleMessages(path: string, locale: string) { public async loadLocaleMessages(path: string, locale: string) {
try { try {
const file = path == '/' ? 'index' : path.replace('/', '').replaceAll('/', '.') const file = path == '/' ? 'index' : path.replace(/^(\/admin\/|\/site\/|\/)/, '').replaceAll('/', '.')
if (this.loadLocale.includes(`${locale}/${file}`)) {
return nextTick()
}
this.loadLocale.push(`${locale}/${file}`)
// 引入语言包文件 // 引入语言包文件
const messages = await import(`./${locale}/${file}.json`) const messages = await import(`./${locale}/${file}.json`)
@ -48,7 +43,8 @@ class Language {
this.i18n.global.mergeLocaleMessage(locale, data) this.i18n.global.mergeLocaleMessage(locale, data)
this.setI18nLanguage(locale) this.setI18nLanguage(locale)
return nextTick() return nextTick()
} catch { } catch (e) {
console.log(e)
this.setI18nLanguage(locale) this.setI18nLanguage(locale)
return nextTick() return nextTick()
} }

View File

@ -31,5 +31,6 @@
"isShowNumber":"是否显示必须是数字", "isShowNumber":"是否显示必须是数字",
"isShowBetween":"是否显示只能是0或者1", "isShowBetween":"是否显示只能是0或者1",
"sortNumber":"排序号必须是数字", "sortNumber":"排序号必须是数字",
"sortBetween":"排序号需要在0-10000之间" "sortBetween":"排序号需要在0-10000之间",
"articleNull":"未读取到文章信息!"
} }

View File

@ -0,0 +1,25 @@
{
"menuName": "菜单名称",
"menuType": "类型",
"authId": "权限标识",
"menuTypeDir": "目录",
"menuTypeMenu": "菜单",
"menuTypeButton": "按钮",
"menuDeleteTips": "确定要删除该菜单吗?",
"addMenu": "添加菜单",
"updateMenu": "编辑菜单",
"routePath": "路由路径",
"viewPath": "组件路径",
"parentMenu": "父级菜单",
"menuIcon": "菜单图标",
"sort":"权重",
"menuKey":"菜单标识",
"menuNamePlaceholder": "请输入菜单名称",
"menuKeyPlaceholder": "请输入菜单标识",
"menuKeyValidata":"菜单标识只能使用字母数字下划线并且开头不能为数字",
"routePathPlaceholder": "请输入路由路径",
"viewPathPlaceholder": "请输入组件路径",
"authIdPlaceholder": "请输入权限标识",
"selectIconPlaceholder": "请选择菜单图标",
"topLevel": "顶级"
}

View File

@ -15,6 +15,8 @@
"lock":"锁定", "lock":"锁定",
"unlock":"解锁", "unlock":"解锁",
"status":"用户状态", "status":"用户状态",
"statusUnlock":"正常",
"statusLock":"锁定",
"userUnlockTips":"确定要解锁该管理员吗?", "userUnlockTips":"确定要解锁该管理员吗?",
"userLockTips":"确定要锁定该管理员吗?" "userLockTips":"确定要锁定该管理员吗?"
} }

View File

@ -1,6 +1,7 @@
{ {
"isOpen": "是否开启", "isOpen": "是否开启",
"h5DomainName": "h5域名", "h5DomainName": "h5域名",
"clickVisit": "点击访问",
"H5Info": "h5配置信息" "H5Info": "h5配置信息"
} }

View File

@ -1,4 +1,7 @@
{ {
"pcInfo": "电脑端信息", "pcInfo": "电脑端信息",
"preview": "预览图" "preview": "预览图",
"copy": "复制",
"clickVisit": "点击访问",
"PCDomainName": "pc域名"
} }

View File

@ -34,6 +34,7 @@
"yes": "是", "yes": "是",
"no": "否", "no": "否",
"copy": "复制", "copy": "复制",
"complete": "完成",
"copySuccess": "复制成功", "copySuccess": "复制成功",
"notSupportCopy": "浏览器不支持一键复制,请手动进行复制", "notSupportCopy": "浏览器不支持一键复制,请手动进行复制",
"selectPlaceholder": "全部", "selectPlaceholder": "全部",
@ -69,7 +70,10 @@
"layoutSetting": "主题设置", "layoutSetting": "主题设置",
"darkMode": "黑暗模式", "darkMode": "黑暗模式",
"sidebarMode": "主题风格", "sidebarMode": "主题风格",
"themeColor": "主题颜色" "themeColor": "主题颜色",
"detectionLoginOperation": "确定",
"detectionLoginContent": "已检测到有其他账号登录,需要刷新后才能继续操作。",
"detectionLoginTip": "提示"
}, },
"axios": { "axios": {
"unknownError": "未知错误", "unknownError": "未知错误",

View File

@ -0,0 +1,39 @@
{
"orderNo":"订单编号",
"orderStatus":"订单状态",
"orderNoPlaceholder":"请输入订单编号",
"createTime":"创建时间",
"rechargeMoney":"充值金额",
"orderMoney":"订单金额",
"member":"买家",
"orderFromName":"订单来源",
"payTypeName":"支付方式",
"startDate":"开始时间",
"endDate":"结束时间",
"namePlaceholder":"请选择",
"applyTime":"申请时间",
"cashOutStatus":"提现状态",
"actualTransferAmount":"实际转账金额",
"cashOutCommission":"提现手续费",
"applicationForWithdrawalAmount":"申请提现金额",
"cashOutMethod":"提现方式",
"cashOutAccountType":"会员账户",
"memberInfo":"会员信息",
"toBeReviewed":"待审核",
"toBeTransferred":"待转账",
"transferred":"已转账",
"turnDown":"拒绝",
"transfer": "转账",
"detail": "详情",
"auditFailure": "审核失败",
"successfulAudit": "审核成功",
"rejectionAudit": "拒绝审核",
"reasonsRefusal": "拒绝理由",
"reasonsRefusalPlaceholder": "请输入拒绝理由",
"isTransfer": "是否确认转账",
"nickname":"会员名称",
"headimg":"会员头像",
"cashOutDetail":"提现详情",
"cashOutMoney": "转账金额"
}

View File

@ -10,5 +10,9 @@
"payTypeName":"支付方式", "payTypeName":"支付方式",
"startDate":"开始时间", "startDate":"开始时间",
"endDate":"结束时间", "endDate":"结束时间",
"namePlaceholder":"请选择" "namePlaceholder":"请选择",
"refundBtn":"退款",
"refundContent":"是否确认退款"
} }

View File

@ -0,0 +1,14 @@
{
"refundNumber":"退款单号",
"userInfo":"用户信息",
"sourceNumber":"来源单号",
"refundAmount":"退款金额",
"refundTime":"退款时间",
"detail": "详情",
"statusName": "状态",
"memberInfo": "会员",
"refundSource": "退款来源",
"startDate":"开始时间",
"endDate":"结束时间",
"refundStatus": "退款状态"
}

View File

@ -1,24 +0,0 @@
{
"orderNo":"订单编号",
"orderStatus":"订单状态",
"orderNoPlaceholder":"请输入订单编号",
"createTime":"创建时间",
"rechargeMoney":"充值金额",
"orderMoney":"订单金额",
"member":"买家",
"orderFromName":"订单来源",
"payTypeName":"支付方式",
"startDate":"开始时间",
"endDate":"结束时间",
"namePlaceholder":"请选择",
"applyTime":"申请时间",
"withdrawalStatus":"提现状态",
"actualTransferAmount":"实际转账金额",
"withdrawalCommission":"提现手续费",
"applicationForWithdrawalAmount":"申请提现金额",
"withdrawalMethod":"提现方式",
"memberInfo":"会员信息",
"toBeTransferred":"待转账",
"transferred":"已转账",
"turnDown":"拒绝"
}

View File

@ -1,9 +1,10 @@
{ {
"dataSummarize": "数据概括",
"todayData": "今日数据", "todayData": "今日数据",
"memberNumb": "会员数", "memberNumb": "新增会员数",
"orderMoney": "订单金额", "orderMoney": "订单金额",
"numberOfSites": "站点数量", "numberOfSites": "站点数量",
"numberOfVisitors": "访客数", "numberOfVisitors": "今日访客数",
"commonlyUsedFunction": "常用功能", "commonlyUsedFunction": "常用功能",
"articleList": "文章列表", "articleList": "文章列表",
"memberManagement": "会员管理", "memberManagement": "会员管理",
@ -20,7 +21,7 @@
"versions": "当前版本", "versions": "当前版本",
"frame": "基于框架", "frame": "基于框架",
"channel": "获取渠道", "channel": "获取渠道",
"serviceSupport": "务支持", "serviceSupport": "官方客服",
"officialWbsite": "官网", "officialWbsite": "官网",
"pageView": "访问量", "pageView": "访问量",
"siteInfo":"站点信息", "siteInfo":"站点信息",
@ -28,5 +29,17 @@
"groupName":"站点套餐", "groupName":"站点套餐",
"expireTime":"过期时间", "expireTime":"过期时间",
"permanent":"永久", "permanent":"永久",
"statusName":"站点状态" "statusName":"站点状态",
"newSiteSum": "新增站点数",
"total": "总计",
"newMemberSum": "新增用户数",
"siteList": "站点列表",
"sitePackage": "站点套餐",
"newSite": "新增站点",
"appMarketplace": "应用市场",
"siteDistribution": "站点分布",
"siteSum": "站点数",
"memberSum": "用户数",
"appSum": "应用数",
"installAppSun": "安装应用数"
} }

View File

@ -1,9 +1,12 @@
{ {
"siteTitle": "Niucloud Admin", "siteTitle": "NIUCLOUD-ADMIN",
"siteDesc": "基于thinkphp6+elementplus+typescript等技术的多端saas通用管理框架采用restful的api接口设计前后端完全分离同时支持多语言开发。", "siteDesc": "基于thinkphp6+elementplus+typescript等技术的多端saas通用管理框架采用restful的api接口设计前后端完全分离同时支持多语言开发。",
"logging": "登录中", "logging": "登录中",
"platform": "管理端", "platform": "管理端",
"login" : "登录", "login" : "登录",
"siteLogin": "站点登录",
"adminLogin": "平台登录",
"userPlaceholder": "请输入您的账号", "userPlaceholder": "请输入您的账号",
"passwordPlaceholder": "请输入您的密码" "passwordPlaceholder": "请输入您的密码",
"manageAdminFramework": "管理系统后台框架"
} }

View File

@ -1,6 +1,6 @@
{ {
"accountData":"金额", "accountData":"金额",
"fromType":"变动类型", "fromType":"来源/用途",
"balanceInfo":"余额变动详情", "balanceInfo":"余额变动详情",
"memo":"备注", "memo":"备注",
"mobile":"手机号码", "mobile":"手机号码",
@ -20,5 +20,8 @@
"memoPlaceholder":"请输入备注信息", "memoPlaceholder":"请输入备注信息",
"addMemberAccountLog":"添加会员账单表", "addMemberAccountLog":"添加会员账单表",
"updateMemberAccountLog":"编辑会员账单表", "updateMemberAccountLog":"编辑会员账单表",
"member_account_logDeleteTips":"确定要删除该会员账单表吗?" "memberAccountLogDeleteTips":"确定要删除该会员账单表吗?",
"balance": "不可提现余额",
"money":"可提现余额",
"balanceType":"账户类型"
} }

View File

@ -0,0 +1,24 @@
{
"accountData":"金额",
"fromType":"来源/用途",
"moneyInfo":"佣金变动详情",
"memo":"备注",
"mobile":"手机号码",
"nickName":"会员信息",
"headimg":"会员头像",
"createTime":"产生时间",
"startDate":"开始时间",
"endDate":"结束时间",
"searchMember":"昵称/手机号",
"searchMemberPlaceholder":"请输入会员昵称/手机号码",
"memberIdPlaceholder":"请输入会员id",
"accountTypePlaceholder":"请输入账户类型",
"accountDataPlaceholder":"请输入账户数据",
"fromTypePlaceholder":"请输入来源类型",
"relatedIdPlaceholder":"请输入关联Id",
"createTimePlaceholder":"请输入创建时间",
"memoPlaceholder":"请输入备注信息",
"addMemberAccountLog":"添加会员账单表",
"updateMemberAccountLog":"编辑会员账单表",
"member_account_logDeleteTips":"确定要删除该会员账单表吗?"
}

View File

@ -24,6 +24,7 @@
"wxOpenid": "微信小程openid", "wxOpenid": "微信小程openid",
"memberLabel": "会员标签", "memberLabel": "会员标签",
"memberLabelPlaceholder": "请选择会员标签", "memberLabelPlaceholder": "请选择会员标签",
"nickNamePlaceholder": "请输入会员名称",
"updateMember": "编辑会员信息", "updateMember": "编辑会员信息",
"notAvailable":"暂无", "notAvailable":"暂无",
"girlSex":"女", "girlSex":"女",
@ -42,6 +43,8 @@
"mobile":"手机号", "mobile":"手机号",
"detail":"详情", "detail":"详情",
"accumulative":"累计", "accumulative":"累计",
"looseChange":"零钱", "money":"可提现余额",
"adjustPoint":"调整积分" "adjustPoint":"调整积分",
"commission":"佣金",
"memberNull":"未读取到会员详情信息"
} }

View File

@ -12,6 +12,6 @@
"updateMemberLabel":"编辑会员标签", "updateMemberLabel":"编辑会员标签",
"sortVerifyOne":"排序要大于零", "sortVerifyOne":"排序要大于零",
"sortVerifyTwo":"排序不能是小数", "sortVerifyTwo":"排序不能是小数",
"member_labelDeleteTips":"确定要删除该会员标签吗?" "memberLabelDeleteTips":"确定要删除该会员标签吗?"
} }

View File

@ -1,9 +1,9 @@
{ {
"registerType":"注册方式", "registerChannel":"注册来源",
"nickname":"会员名称", "nickname":"会员名称",
"mobile":"手机号", "mobile":"手机号",
"createTime":"注册时间", "createTime":"注册时间",
"lastVisitTime":"最后登录时间", "lastVisitTime":"最后访问时间",
"addMember":"添加会员", "addMember":"添加会员",
"updateMember":"编辑会员", "updateMember":"编辑会员",
"nickNamePlaceholder":"请输入会员名称", "nickNamePlaceholder":"请输入会员名称",
@ -39,9 +39,11 @@
"doubleCipherHint": "输入的两次密码不一致", "doubleCipherHint": "输入的两次密码不一致",
"mobileHint": "请输入正确的手机号!", "mobileHint": "请输入正确的手机号!",
"lable": "标签", "lable": "标签",
"setLable": "设置标签", "setLable": "标签",
"notAvailable": "暂无", "notAvailable": "暂无",
"memberLabelPlaceholder": "请选择会员标签", "memberLabelPlaceholder": "请选择会员标签",
"memberInfo":"会员信息", "memberInfo":"会员信息",
"memberInfoPlaceholder":"请输入会员名称/会员昵称/手机号" "memberInfoPlaceholder":"请输入会员名称/会员昵称/手机号",
"lock": "锁定",
"scrubLock": "取消锁定"
} }

View File

@ -0,0 +1,7 @@
{
"isCaptcha": "是否启用验证码",
"bgImg": "背景图片",
"admin": "平台端",
"isBgImgTip": "建议上传尺寸为450*400px",
"site": "站点端"
}

View File

@ -1,13 +1,13 @@
{ {
"siteId":"站点id", "siteId":"站点id",
"messageKey":"消息模板", "noticeKey":"消息模板",
"messageType":"消息类型", "noticeType":"消息类型",
"messageInfo":"消息详情", "noticeInfo":"消息详情",
"uid":"通知的用户id", "uid":"通知的用户id",
"memberId":"消息的会员id", "memberId":"消息的会员id",
"nickname":"接收会员", "nickname":"接收会员",
"receiver":"手机/OPENID", "receiver":"手机/OPENID",
"messageData":"消息内容", "noticeData":"消息内容",
"isClick":"点击次数", "isClick":"点击次数",
"searchReceiver":"接收人", "searchReceiver":"接收人",
"isVisit":"访问次数", "isVisit":"访问次数",
@ -17,16 +17,16 @@
"sms":"短信", "sms":"短信",
"weapp":"微信小程序", "weapp":"微信小程序",
"wechat":"微信公众号", "wechat":"微信公众号",
"messageTypePlaceholder":"请选择消息类型", "noticeTypePlaceholder":"请选择消息类型",
"uidPlaceholder":"请输入通知的用户id", "uidPlaceholder":"请输入通知的用户id",
"memberIdPlaceholder":"请输入消息的会员id", "memberIdPlaceholder":"请输入消息的会员id",
"nicknamePlaceholder":"请输入接收人用户昵称或姓名", "nicknamePlaceholder":"请输入接收人用户昵称或姓名",
"receiverPlaceholder":"请输入接收人手机号/openid", "receiverPlaceholder":"请输入接收人手机号/openid",
"messageDataPlaceholder":"请输入消息数据", "noticeDataPlaceholder":"请输入消息数据",
"isClickPlaceholder":"请输入点击次数", "isClickPlaceholder":"请输入点击次数",
"isVisitPlaceholder":"请输入访问次数", "isVisitPlaceholder":"请输入访问次数",
"visitTimePlaceholder":"请输入访问时间", "visitTimePlaceholder":"请输入访问时间",
"createTimePlaceholder":"请输入消息时间", "createTimePlaceholder":"请输入消息时间",
"buyerMessage": "会员消息", "buyerNotice": "会员消息",
"sellerMessage":"平台消息" "sellerNotice":"平台消息"
} }

View File

@ -1,10 +1,10 @@
{ {
"buyerMessage": "会员消息", "buyerNotice": "会员消息",
"sellerMessage":"平台消息", "sellerNotice":"平台消息",
"sms":"短信", "sms":"短信",
"weapp":"微信小程序", "weapp":"微信小程序",
"wechat":"微信公众号", "wechat":"微信公众号",
"messageSetting":"消息设置", "noticeSetting":"消息设置",
"name": "模板名称", "name": "模板名称",
"title": "场景", "title": "场景",
"smsContent": "短信内容", "smsContent": "短信内容",

View File

@ -34,5 +34,20 @@
"appIdTips": "支付宝分配给开发者的应用ID", "appIdTips": "支付宝分配给开发者的应用ID",
"appPublicCertPathTips": "上传appCertPublicKey文件", "appPublicCertPathTips": "上传appCertPublicKey文件",
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件", "alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
"alipayRootCertPathTips": "上传alipayRootCert文件" "alipayRootCertPathTips": "上传alipayRootCert文件",
"payType": "支付方式",
"templateName": "配置信息",
"settingDefaultPay": "设置默认支付",
"settingDefault": "设置默认",
"defaultTamplate": "请选择模板",
"isEnable": "是否启用",
"onState": "开启状态",
"configurePaymentMethod": "请先配置值该支付方式",
"enablePaymentMode": "请先开启该支付方式",
"clickConfigure": "点击配置",
"setConfig": "设置支付配置",
"open": "开启",
"notOpen": "未开启",
"cancel": "取消"
} }

View File

@ -0,0 +1,31 @@
{
"wechatpay": "微信打款设置",
"alipay": "支付宝打款设置",
"mchId": "商户号",
"mchIdPlaceholder": "请输入商户号",
"mchIdTips": "微信支付商户号MCHID",
"mchSecretKey": "API密钥",
"mchSecretKeyPlaceholder": "请输入API密钥",
"mchSecretKeyTips": "微信支付商户API密钥paySignKey",
"mchSecretCert": "商户私钥",
"mchSecretCertPlaceholder": "请上传商户私钥",
"mchSecretCertTips": "微信支付API证书apiclient_key.pem",
"mchPublicCertPath": "商户公钥",
"mchPublicCertPathPlaceholder": "请上传商户公钥",
"mchPublicCertPathTips": "微信支付API证书apiclient_cert.pem",
"appId": "app_id",
"appIdPlaceholder": "请输入app_id",
"appSecretCert": "应用私钥",
"appSecretCertPlaceholder": "请输入应用私钥",
"appPublicCertPath": "应用公钥",
"appPublicCertPathPlaceholder": "请上传应用公钥",
"alipayPublicCertPath": "支付宝公钥",
"alipayPublicCertPathPlaceholder": "请上传支付宝公钥",
"alipayRootCertPath": "支付宝根证书",
"alipayRootCertPathPlaceholder": "请上传支付宝根证书",
"appIdTips": "支付宝分配给开发者的应用ID",
"appPublicCertPathTips": "上传appCertPublicKey文件",
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
"alipayRootCertPathTips": "上传alipayRootCert文件",
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景"
}

View File

@ -0,0 +1,32 @@
{
"siteId":"站点id",
"noticeKey":"消息模板",
"noticeType":"消息类型",
"noticeInfo":"消息详情",
"uid":"通知的用户id",
"memberId":"消息的会员id",
"nickname":"接收会员",
"receiver":"手机号",
"noticeData":"消息内容",
"isClick":"点击次数",
"searchReceiver":"接收人",
"isVisit":"访问次数",
"createTime":"发送时间",
"startDate":"开始时间",
"endDate":"结束时间",
"sms":"短信",
"weapp":"微信小程序",
"wechat":"微信公众号",
"noticeTypePlaceholder":"请选择消息类型",
"uidPlaceholder":"请输入通知的用户id",
"memberIdPlaceholder":"请输入消息的会员id",
"nicknamePlaceholder":"请输入接收人用户昵称或姓名",
"receiverPlaceholder":"请输入接收人手机号",
"noticeDataPlaceholder":"请输入消息数据",
"isClickPlaceholder":"请输入点击次数",
"isVisitPlaceholder":"请输入访问次数",
"visitTimePlaceholder":"请输入访问时间",
"createTimePlaceholder":"请输入消息时间",
"buyerNotice": "会员消息",
"sellerNotice":"平台消息"
}

View File

@ -3,18 +3,18 @@
"updateTime": "修改时间", "updateTime": "修改时间",
"createTime": "创建时间", "createTime": "创建时间",
"groupName": "套餐名称", "groupName": "套餐名称",
"groupDesc": "套餐介绍", "groupDesc": "套餐说明",
"groupRoles": "套餐权限", "groupRoles": "套餐权限",
"groupDeleteTips": "确定要删除该套餐吗?", "groupDeleteTips": "确定要删除该套餐吗?",
"groupNamePlaceholder": "请输入套餐名称", "groupNamePlaceholder": "请输入套餐名称",
"groupDescPlaceholder": "请输入套餐介绍", "groupDescPlaceholder": "请输入套餐说明",
"groupRolesPlaceholder": "请选择套餐权限", "groupRolesPlaceholder": "请选择套餐权限",
"groupPlaceholder": "请选择权限", "groupPlaceholder": "请选择权限",
"permission": "权限", "permission": "权限",
"updateGroup":"编辑套餐", "updateGroup":"编辑套餐",
"addGroup":"添加套餐", "addGroup":"添加套餐",
"checkStrictly": "父子级不关联", "checkStrictly": "父子级不关联",
"remark": "备注", "remark": "套餐说明",
"reset": "重置", "reset": "重置",
"search": "搜索" "search": "搜索"
} }

View File

@ -5,7 +5,7 @@
"status":"状态", "status":"状态",
"businessHours":"营业时间", "businessHours":"营业时间",
"createTime":"创建时间", "createTime":"创建时间",
"expireTime":"期时间", "expireTime":"期时间",
"siteNamePlaceholder":"请输入站点名称", "siteNamePlaceholder":"请输入站点名称",
"createTimePlaceholder":"请输入创建时间", "createTimePlaceholder":"请输入创建时间",
"addSite":"添加站点", "addSite":"添加站点",
@ -24,5 +24,7 @@
"confirmPasswordPlaceholder": "请再次确认密码", "confirmPasswordPlaceholder": "请再次确认密码",
"userRealNamePlaceholder": "请输入名称", "userRealNamePlaceholder": "请输入名称",
"expireTimePlaceholder":"请选择到期时间", "expireTimePlaceholder":"请选择到期时间",
"confirmPasswordError": "两次输入的密码不一致" "confirmPasswordError": "两次输入的密码不一致",
"operationTip": "温馨提示:站点登录页面"
} }

View File

@ -1,9 +1,9 @@
{ {
"todayData": "今日数据", "todayData": "今日数据",
"memberNumb": "会员数", "memberNumb": "新增会员数",
"orderMoney": "订单金额", "orderMoney": "订单金额",
"numberOfSites": "站点数量", "numberOfSites": "站点数量",
"numberOfVisitors": "访客数", "numberOfVisitors": "今日访客数",
"commonlyUsedFunction": "常用功能", "commonlyUsedFunction": "常用功能",
"articleList": "文章列表", "articleList": "文章列表",
"memberManagement": "会员管理", "memberManagement": "会员管理",

View File

@ -1,3 +1,12 @@
{ {
"content":"功能正在开发,请敬请期待..." "noPlug":"暂无插件",
"install":"安装",
"unload":"卸载",
"installLabel":"已安装",
"uninstalledLabel":"未安装",
"version":"版本",
"title":"名称",
"desc":"描述",
"plugDetail": "插件信息",
"author": "作者"
} }

View File

@ -0,0 +1,15 @@
{
"title": "名称",
"serverInformation": "服务器信息",
"systemDemand": "系统环境要求",
"authorityStatus": "权限状态",
"name": "选项",
"demand": "要求",
"status": "状态",
"environment": "环境",
"version": "版本",
"phpType": "PHP版本",
"phpTypeValue": "大于等于8.0.0",
"mysqlType": "mysql版本",
"mysqlTypeValue": "大于等于5.7"
}

View File

@ -0,0 +1,7 @@
{
"refresh":"刷新",
"refreshMenu":"刷新菜单",
"refreshMenuDesc":"新增/修改插件菜单后,需要刷新插件菜单",
"dataCache":"数据缓存",
"dataCacheDesc":"新增/修改数据表后,需要清除数据表缓存"
}

View File

@ -14,8 +14,10 @@
</div> </div>
<!-- 返回上一页 --> <!-- 返回上一页 -->
<div class="flex items-center cursor-pointer" v-if="appStore.pageReturn" @click="backFn"> <div class="flex items-center cursor-pointer" v-if="appStore.pageReturn" @click="backFn">
<el-icon class="mr-1"><Back /></el-icon> <el-icon class="mr-1">
<span class="text-base mr-3">{{t('returnToPreviousPage')}}</span> <Back />
</el-icon>
<span class="text-base mr-3">{{ t('returnToPreviousPage') }}</span>
<span class=" text-gray-300">|</span> <span class=" text-gray-300">|</span>
</div> </div>
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
@ -30,8 +32,9 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<div class="right-panel h-full flex items-center justify-end"> <div class="right-panel h-full flex items-center justify-end">
<!-- 预览 --> <!-- 预览 只有站点时展示-->
<img class="w-[16px] h-[16px] mr-1" src="@/assets/images/icon_preview.png" :alt="t('preview')" :title="t('preview')"> <img class="w-[16px] h-[16px] mr-1" v-if="appType == 'site'" src="@/assets/images/icon_preview.png"
:alt="t('preview')" :title="t('preview')">
<!-- 切换语言 --> <!-- 切换语言 -->
<div class="navbar-item flex items-center h-full cursor-pointer"> <div class="navbar-item flex items-center h-full cursor-pointer">
<switch-lang /> <switch-lang />
@ -52,6 +55,18 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<input type="hidden" v-model="comparisonToken">
<input type="hidden" v-model="comparisonSiteId">
<el-dialog v-model="detectionLoginDialog" :title="t('layout.detectionLoginTip')" width="30%"
:close-on-click-modal="false" :close-on-press-escape="false" :show-close="false">
<span>{{ t('layout.detectionLoginContent') }}</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="detectionLoginFn">{{ t('layout.detectionLoginOperation') }}</el-button>
</span>
</template>
</el-dialog>
</el-container> </el-container>
</template> </template>
@ -65,17 +80,43 @@ import useSystemStore from '@/stores/modules/system'
import useAppStore from '@/stores/modules/app' import useAppStore from '@/stores/modules/app'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { t } from '@/lang' import { t } from '@/lang'
import storage from '@/utils/storage'
import useUserStore from '@/stores/modules/user'
const router = useRouter() const router = useRouter()
const appType = storage.get('app_type')
const { toggle: toggleFullscreen, isFullscreen } = useFullscreen() const { toggle: toggleFullscreen, isFullscreen } = useFullscreen()
const systemStore = useSystemStore() const systemStore = useSystemStore()
const appStore = useAppStore() const appStore = useAppStore()
const route = useRoute() const route = useRoute()
const screenWidth = ref(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) const screenWidth = ref(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth)
// start
const detectionLoginDialog = ref(false)
const comparisonToken = ref('')
const comparisonSiteId = ref('')
if (storage.get('comparisonTokenStorage')) {
comparisonToken.value = storage.get('comparisonTokenStorage')
// storage.remove(['comparisonTokenStorage']);
}
if (storage.get('comparisonSiteIdStorage')) {
comparisonSiteId.value = storage.get('comparisonSiteIdStorage')
// storage.remove(['comparisonSiteIdStorage']);
}
//
document.addEventListener('visibilitychange', e => {
if (document.visibilityState === 'visible' && (comparisonSiteId.value != storage.get('siteId') || comparisonToken.value != storage.get('token'))) {
detectionLoginDialog.value = true
}
})
const detectionLoginFn = () => {
detectionLoginDialog.value = false
location.reload();
}
// end
//
onMounted(() => { onMounted(() => {
//
window.onresize = () => { window.onresize = () => {
return (() => { return (() => {
screenWidth.value = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth screenWidth.value = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
@ -111,15 +152,15 @@ const refreshRouter = () => {
// //
const breadcrumb = computed(() => { const breadcrumb = computed(() => {
const matched = route.matched const matched = route.matched.filter(item => { return item.meta.title })
if (matched[0] && matched[0].path == '/') matched.splice(0, 1) if (matched[0] && matched[0].path == '/') matched.splice(0, 1)
return matched return matched
}) })
// //
const backFn = ()=>{ const backFn = () => {
router.go(-1); router.go(-1)
}; }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -129,5 +170,4 @@ const backFn = ()=>{
&:hover { &:hover {
background-color: var(--el-bg-color-page); background-color: var(--el-bg-color-page);
} }
} }</style>
</style>

View File

@ -1,18 +1,29 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory, RouteLocationRaw } from 'vue-router'
import NProgress from 'nprogress' import NProgress from 'nprogress'
import 'nprogress/nprogress.css' import 'nprogress/nprogress.css'
import { STATIC_ROUTES, NO_LOGIN_ROUTES, INDEX_ROUTE, findFirstValidRoute, DECORATE_ROUTER } from './routers' import { STATIC_ROUTES, NO_LOGIN_ROUTES, ROOT_ROUTER, ADMIN_ROUTE, SITE_ROUTE, DECORATE_ROUTER, findFirstValidRoute } from './routers'
import { language } from '@/lang' import { language } from '@/lang'
import useSystemStore from '@/stores/modules/system' import useSystemStore from '@/stores/modules/system'
import useUserStore from '@/stores/modules/user' import useUserStore from '@/stores/modules/user'
import { setWindowTitle } from '@/utils/common' import { setWindowTitle, getAppType, urlToRouteRaw } from '@/utils/common'
import storage from '@/utils/storage' import storage from '@/utils/storage'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: STATIC_ROUTES, routes: [...STATIC_ROUTES, ADMIN_ROUTE, SITE_ROUTE]
}) })
/**
* push方法
*/
const originPush = router.push
router.push = (to: RouteLocationRaw) => {
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 originPush(route)
}
// 全局前置守卫 // 全局前置守卫
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
@ -23,19 +34,22 @@ router.beforeEach(async (to, from, next) => {
const userStore = useUserStore() const userStore = useUserStore()
const siteInfo = storage.get('siteInfo') || false const siteInfo = storage.get('siteInfo') || false
let title = (to.meta?.title || '') + (siteInfo.site_name ? ('-' + siteInfo.site_name) : '') let title = (to.meta?.title || '') + (siteInfo.site_name ? ('-' + siteInfo.site_name) : '')
// 设置网站标题 // 设置网站标题
setWindowTitle(title) setWindowTitle(title)
// 加载语言包 // 加载语言包
await language.loadLocaleMessages(to.path, useSystemStore().lang); await language.loadLocaleMessages(to.path, useSystemStore().lang);
const loginPath = to.path == '/' ? '/admin/login' : `${to.matched[0].path}/login`
// 判断是否需登录 // 判断是否需登录
if (NO_LOGIN_ROUTES.includes(to.path)) { if (NO_LOGIN_ROUTES.includes(to.path)) {
next() next()
} else if (userStore.token) { } else if (userStore.token) {
// 如果已加载路由 // 如果已加载路由
if (userStore.routers.length) { if (userStore.routers.length) {
if (to.path === '/login') { if (to.path === loginPath) {
next('/') next(`/${getAppType()}`)
} else { } else {
next() next()
} }
@ -45,8 +59,17 @@ router.beforeEach(async (to, from, next) => {
// 设置首页路由 // 设置首页路由
const firstRoute = findFirstValidRoute(userStore.routers) const firstRoute = findFirstValidRoute(userStore.routers)
INDEX_ROUTE.redirect = { name: firstRoute } ROOT_ROUTER.redirect = { name: firstRoute }
router.addRoute(INDEX_ROUTE) router.addRoute(ROOT_ROUTER)
// 设置应用首页路由
if (getAppType() == 'admin') {
ADMIN_ROUTE.children[0].redirect = { name: firstRoute }
router.addRoute(ADMIN_ROUTE)
} else {
SITE_ROUTE.children[0].redirect = { name: firstRoute }
router.addRoute(SITE_ROUTE)
}
// 添加动态路由 // 添加动态路由
userStore.routers.forEach(route => { userStore.routers.forEach(route => {
@ -56,25 +79,35 @@ router.beforeEach(async (to, from, next) => {
router.addRoute(DECORATE_ROUTER) router.addRoute(DECORATE_ROUTER)
return return
} }
if (!route.children) { if (!route.children) {
router.addRoute('root', route) if (route.meta.app == 'admin') {
router.addRoute(ADMIN_ROUTE.children[0].name, route)
} else {
router.addRoute(SITE_ROUTE.children[0].name, route)
}
return return
} }
// 动态添加可访问路由表 // 动态添加可访问路由表
router.addRoute(route) if (route.meta.app == 'admin') {
router.addRoute(ADMIN_ROUTE.name, route)
} else {
router.addRoute(SITE_ROUTE.name, route)
}
}) })
next({ path: to.path, query: to.query, replace: true }) next({ path: to.path, query: to.query, replace: true })
} catch (err) { } catch (err) {
userStore.logout() userStore.logout()
next({ path: '/login', query: { redirect: to.fullPath } }) next({ path: loginPath, query: { redirect: to.fullPath } })
} }
} }
} else { } else {
if (to.path === '/login') { if (to.path === loginPath) {
next() next()
} else { } else {
next({ path: '/login', query: { redirect: to.fullPath } }) next({ path: loginPath, query: { redirect: to.fullPath } })
} }
} }
}) })

View File

@ -4,25 +4,40 @@ import Decorate from '@/layout/decorate/index.vue'
// 静态路由 // 静态路由
export const STATIC_ROUTES: Array<RouteRecordRaw> = [ export const STATIC_ROUTES: Array<RouteRecordRaw> = [
{
path: `${import.meta.env.BASE_URL}`,
component: Default,
name: 'root'
},
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404.vue') component: () => import('@/views/error/404.vue')
}
]
// 免登录路由
export const NO_LOGIN_ROUTES: string[] = [
'/404'
]
// 根路由
export const ROOT_ROUTER: RouteRecordRaw = {
path: '/',
component: Default,
name: Symbol()
}
// 平台端根路由
export const ADMIN_ROUTE: RouteRecordRaw = {
path: '/admin',
name: Symbol('admin'),
children: [
{
path: '',
name: Symbol('adminRoot'),
component: Default
}, },
{ {
path: '/403', path: 'login',
component: () => import('@/views/error/403.vue')
},
{
path: '/login',
component: () => import('@/views/login/index.vue') component: () => import('@/views/login/index.vue')
}, },
{ {
path: '/user', path: 'user',
component: Default, component: Default,
children: [ children: [
{ {
@ -35,19 +50,38 @@ export const STATIC_ROUTES: Array<RouteRecordRaw> = [
} }
] ]
} }
] ]
}
// 免登录路由 // 站点端路由
export const NO_LOGIN_ROUTES: string[] = [ export const SITE_ROUTE: RouteRecordRaw = {
'/403', path: '/site',
'/404' name: Symbol('site'),
] children: [
{
// 首页路由 path: '',
export const INDEX_ROUTE: RouteRecordRaw = { name: Symbol('siteRoot'),
path: '/', component: Default
},
{
path: 'login',
component: () => import('@/views/login/index.vue')
},
{
path: 'user',
component: Default, component: Default,
name: Symbol() children: [
{
path: 'center',
meta: {
type: 1,
title: '个人中心'
},
component: () => import('@/views/index/personal.vue')
}
]
}
]
} }
// 装修路由 // 装修路由
@ -70,7 +104,8 @@ interface Route {
type: string type: string
}, },
children?: [], children?: [],
is_show: boolean is_show: boolean,
app_type: string
} }
/** /**
@ -80,13 +115,14 @@ interface Route {
*/ */
const createRoute = function (route: Route, parentRoute: RouteRecordRaw | null = null): RouteRecordRaw { const createRoute = function (route: Route, parentRoute: RouteRecordRaw | null = null): RouteRecordRaw {
const record: RouteRecordRaw = { const record: RouteRecordRaw = {
path: parentRoute ? route.router_path : '/' + route.router_path, path: parentRoute ? route.router_path : route.router_path != 'decorate' ? `/${route.app_type}/${route.router_path}` : `/${route.router_path}`,
name: parentRoute ? Symbol(`${parentRoute.path}/${route.router_path}`) : Symbol(`/${route.router_path}`), name: parentRoute ? Symbol(`${parentRoute.path}/${route.router_path}`) : Symbol(`/${route.router_path}`),
meta: { meta: {
title: route.menu_name, title: route.menu_name,
icon: route.icon, icon: route.icon,
type: route.menu_type, type: route.menu_type,
show: route.is_show show: route.is_show,
app: route.app_type
} }
} }
if (route.menu_type == 0) { if (route.menu_type == 0) {

View File

@ -50,20 +50,19 @@ const useDiyStore = defineStore('diy', {
}, },
actions: { actions: {
// 添加组件 // 添加组件
addComponent(data: any) { addComponent(key: string, data: any) {
// 加载完才能添加组件 // 加载完才能添加组件
if(!this.load) return; if (!this.load) return;
// 删除不用的字段 // 删除不用的字段
let component = cloneDeep(data); let component = cloneDeep(data);
component.id = this.generateRandom(); component.id = this.generateRandom();
component.componentName = component.name; component.componentName = key;
component.componentTitle = component.title; component.componentTitle = component.title;
component.maxCount = component.max_count; component.maxCount = component.max_count;
Object.assign(component, component.value); Object.assign(component, component.value);
delete component.name;
delete component.title; delete component.title;
delete component.value; delete component.value;
delete component.type; delete component.type;

View File

@ -1,9 +1,9 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { getToken, setToken, removeToken } from '@/utils/common' import { getToken, setToken, removeToken, getAppType } from '@/utils/common'
import { login, getAuthMenus } from '@/api/auth' import { login, getAuthMenus } from '@/api/auth'
import storage from '@/utils/storage' import storage from '@/utils/storage'
import router from '@/router' import router from '@/router'
import { formatRouters, INDEX_ROUTE } from '@/router/routers' import { formatRouters } from '@/router/routers'
import useTabbarStore from './tabbar' import useTabbarStore from './tabbar'
interface User { interface User {
@ -25,9 +25,9 @@ const useSystemStore = defineStore('user', {
} }
}, },
actions: { actions: {
login(form: object) { login(form: object, app_type: any) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
login(form) login(form, app_type)
.then((res) => { .then((res) => {
this.token = res.data.token this.token = res.data.token
this.userInfo = res.data.userinfo this.userInfo = res.data.userinfo
@ -35,6 +35,8 @@ const useSystemStore = defineStore('user', {
storage.set({ key: 'userinfo', data: res.data.userinfo }) 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_id })
storage.set({ key: 'siteInfo', data: res.data.site_info }) storage.set({ key: 'siteInfo', data: res.data.site_info })
storage.set({ key: 'comparisonSiteIdStorage', data: res.data.site_id })
storage.set({ key: 'comparisonTokenStorage', data: res.data.token })
resolve(res) resolve(res)
}) })
.catch((error) => { .catch((error) => {
@ -48,15 +50,10 @@ const useSystemStore = defineStore('user', {
this.siteInfo = {} this.siteInfo = {}
removeToken() removeToken()
storage.remove(['userinfo', 'siteId', 'siteInfo']) storage.remove(['userinfo', 'siteId', 'siteInfo'])
// 清除路由
this.routers.forEach(item => {
router.removeRoute(item.name)
})
router.removeRoute(INDEX_ROUTE.name)
this.routers = [] this.routers = []
// 清除tabbar // 清除tabbar
useTabbarStore().clearTab() useTabbarStore().clearTab()
router.push('/login') router.push(`/${getAppType()}/login`)
}, },
getAuthMenus() { getAuthMenus() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 3883393 */ font-family: "iconfont"; /* Project id 3883393 */
src: url('//at.alicdn.com/t/c/font_3883393_c2tb4v0fz24.woff2?t=1681374762000') format('woff2'), src: url('//at.alicdn.com/t/c/font_3883393_4jlgm3tby7.woff2?t=1683947082967') format('woff2'),
url('//at.alicdn.com/t/c/font_3883393_c2tb4v0fz24.woff?t=1681374762000') format('woff'), url('//at.alicdn.com/t/c/font_3883393_4jlgm3tby7.woff?t=1683947082967') format('woff'),
url('//at.alicdn.com/t/c/font_3883393_c2tb4v0fz24.ttf?t=1681374762000') format('truetype'); url('//at.alicdn.com/t/c/font_3883393_4jlgm3tby7.ttf?t=1683947082967') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,46 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icontuikuanjilu:before {
content: "\e8cf";
}
.icongengxinhuancun:before {
content: "\e686";
}
.iconsixingjiance:before {
content: "\e645";
}
.iconzhuceshezhi:before {
content: "\e6ad";
}
.iconmanage-apply:before {
content: "\e619";
}
.iconyingyongguanli:before {
content: "\e61f";
}
.iconkaifazheguanli:before {
content: "\e624";
}
.iconlianmengguanli:before {
content: "\e65f";
}
.icondianzan:before {
content: "\ec7f";
}
.iconh5e:before {
content: "\e654";
}
.iconyingyongshichang:before { .iconyingyongshichang:before {
content: "\e618"; content: "\e618";
} }

View File

@ -1,8 +1,9 @@
import type {App} from 'vue' import type { App } from 'vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue' import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import {useCssVar, useTitle} from '@vueuse/core' import { useCssVar, useTitle } from '@vueuse/core'
import colorFunction from 'css-color-function' import colorFunction from 'css-color-function'
import storage from './storage' import storage from './storage'
import { useRoute } from 'vue-router'
/** /**
* element-icon * element-icon
@ -45,6 +46,20 @@ export function setThemeColor(color: string, mode: string = 'light'): void {
}) })
} }
/**
* 访
*/
export function getAppType() {
const path = location.pathname.split('/').filter((val) => { return val })
if (!path.length) {
return 'admin'
} else {
const app = path[0]
return app == 'decorate' ? 'site' : app
}
}
/** /**
* title * title
* @param value * @param value
@ -68,7 +83,7 @@ export function getToken(): null | string {
* @returns * @returns
*/ */
export function setToken(token: string): void { export function setToken(token: string): void {
storage.set({key: 'token', data: token}) storage.set({ key: 'token', data: token })
} }
/** /**
@ -143,3 +158,19 @@ export function getWapDomain(): string {
return (import.meta.env.VITE_WAP_DOMAIN || location.origin + '/wap') + (storage.get('siteId') == 1 ? '' : '/s' + storage.get('siteId')); return (import.meta.env.VITE_WAP_DOMAIN || location.origin + '/wap') + (storage.get('siteId') == 1 ? '' : '/s' + storage.get('siteId'));
} }
} }
/**
* url route
* @param url
*/
export function urlToRouteRaw(url: string) {
const query = {}
const [path, param] = url.split('?')
param && param.split('&').forEach((str : string) => {
let [name, value] = str.split('=')
query[name] = value
})
return { path, query }
}

View File

@ -52,7 +52,7 @@ class Request {
this.instance.interceptors.response.use( this.instance.interceptors.response.use(
(response: requestResponse) => { (response: requestResponse) => {
const res = response.data const res = response.data
if (res.code != 200) { if (res.code != 1) {
this.handleAuthError(res.code) this.handleAuthError(res.code)
if (response.config.showErrorMessage) ElMessage({ message: res.msg, type: 'error' }) if (response.config.showErrorMessage) ElMessage({ message: res.msg, type: 'error' })
return Promise.reject(new Error(res.msg || 'Error')) return Promise.reject(new Error(res.msg || 'Error'))

View File

@ -1,3 +1,5 @@
import { getAppType } from './common'
interface setParam { interface setParam {
key: string, key: string,
data: any, data: any,
@ -6,6 +8,11 @@ interface setParam {
} }
class Storage { class Storage {
private prefix = ''
public constructor() {
this.prefix = getAppType()
}
/** /**
* *
@ -13,7 +20,7 @@ class Storage {
*/ */
public set(param: setParam) { public set(param: setParam) {
try { try {
window.localStorage.setItem(param.key, JSON.stringify(param.data)) window.localStorage.setItem(`${this.prefix}.${param.key}`, JSON.stringify(param.data))
typeof param.success == 'function' && param.success() typeof param.success == 'function' && param.success()
} catch (error) { } catch (error) {
typeof param.fail == 'function' && param.fail(error) typeof param.fail == 'function' && param.fail(error)
@ -27,7 +34,7 @@ class Storage {
*/ */
public get(key: string) { public get(key: string) {
try { try {
const json: any = window.localStorage.getItem(key) const json: any = window.localStorage.getItem(`${this.prefix}.${key}`)
return JSON.parse(json) return JSON.parse(json)
} catch (error) { } catch (error) {
return null return null
@ -39,8 +46,8 @@ class Storage {
* @param key * @param key
*/ */
public remove(key: string | string[]) { public remove(key: string | string[]) {
if (typeof key == 'string') window.localStorage.removeItem(key) if (typeof key == 'string') window.localStorage.removeItem(`${this.prefix}.${key}`)
else key.forEach(item => { window.localStorage.removeItem(item) }) else key.forEach(item => { window.localStorage.removeItem(`${this.prefix}.${item}`) })
} }
/** /**

View File

@ -0,0 +1,63 @@
<template>
<div class="main-container bg-[#fff] min-w-[1000px] min-h-[650px]">
<div class="pt-[1px]" v-for="(listItems, listIndex) in appManageList" :key="listIndex">
<p class="ml-4 mt-[20px] border-l-[2px] border-[#273de3] pl-4 leading-[1]">{{ listItems.name }}</p>
<div class="flex flex-wrap" v-if="isTrue == 0">
<div @click="toLink(appItems.url)" class="flex cursor-pointer ml-4 mt-[20px] w-[240px] p-[10px] border-[1px] border-[#eee] border-[solid]" v-for="(appItems, appIndex) in listItems.app_list" :key="appIndex">
<div class="flex justify-center items-center mr-[10px] min-w-[50px]">
<img v-if="appItems.icon" class="w-[50px] h-[50px]" :src="img(appItems.icon)"/>
</div>
<div class="w-[150px]">
<p class="app-text text-[14px] text-[#666]">{{ appItems.title }}</p>
<p class="app-text text-[12px] text-[#999]">{{ appItems.desc }}</p>
</div>
</div>
</div>
<div class="flex flex-wrap" v-if="isTrue == 1">
<div @click="toLink(appItems.url)" class="cursor-pointer ml-4 mt-[20px] w-[140px] p-[10px] border-[1px] border-[#eee] border-[solid]" v-for="(appItems, appIndex) in listItems.app_list" :key="appIndex">
<div class="flex justify-center items-center">
<img v-if="appItems.cover" class="w-[110px] h-[110px]" :src="img(appItems.cover)"/>
</div>
<div class="text-center mt-2">
<p class="app-text text-[14px] text-[#666]">{{ appItems.title }}</p>
<p class="app-text text-[12px] text-[#999]">{{ appItems.desc }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getAppMange } from '@/api/sys'
import { img } from '@/utils/common'
import { useRouter } from 'vue-router'
const appManageList = ref([])
const isTrue = ref(1)
const checkAppMange = () => {
getAppMange().then(res=>{
appManageList.value = res.data;
})
}
checkAppMange()
const router = useRouter()
/**
* 链接跳转
*/
const toLink = (link) => {
router.push(link)
}
</script>
<style lang="scss" scoped>
.app-text {
overflow:hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow:ellipsis;
}
</style>

View File

@ -1,7 +1,7 @@
<template> <template>
<el-dialog v-model="showDialog" :title="popTitle" width="500px" <el-dialog v-model="showDialog" :title="popTitle" width="500px" :destroy-on-close="true">
:destroy-on-close="true"> <el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form"
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading"> v-loading="loading">
<el-form-item :label="t('name')" prop="name"> <el-form-item :label="t('name')" prop="name">
<el-input v-model="formData.name" clearable :placeholder="t('namePlaceholder')" class="input-width" /> <el-input v-model="formData.name" clearable :placeholder="t('namePlaceholder')" class="input-width" />
</el-form-item> </el-form-item>
@ -32,9 +32,9 @@
import { ref, reactive, computed } from 'vue' import { ref, reactive, computed } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { addArticleCategory, updateArticleCategory, getArticleCategoryInfo } from '@/api/article' import { addArticleCategory, editArticleCategory, getArticleCategoryInfo } from '@/api/article'
let popTitle:string = ''; let popTitle: string = '';
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(true) const loading = ref(true)
@ -42,7 +42,7 @@ const loading = ref(true)
/** /**
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
category_id: '', category_id: '',
name: '', name: '',
sort: '', sort: '',
@ -71,7 +71,7 @@ const formRules = computed(() => {
sort: [ sort: [
{ {
validator: (rule: any, value: string, callback: any) => { validator: (rule: any, value: string, callback: any) => {
if(value === "" || isNaN(value)){ if (value === "" || isNaN(value)) {
callback(new Error(t('sortNumber'))) callback(new Error(t('sortNumber')))
} }
if (parseInt(value) > 10000) { if (parseInt(value) > 10000) {
@ -94,7 +94,7 @@ const emit = defineEmits(['complete'])
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.category_id ? updateArticleCategory : addArticleCategory let save = formData.category_id ? editArticleCategory : addArticleCategory
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {

View File

@ -1,7 +1,8 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading"> <el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form"
v-loading="loading">
<el-form-item :label="t('title')" prop="title"> <el-form-item :label="t('title')" prop="title">
<el-input v-model="formData.title" clearable :placeholder="t('titlePlaceholder')" class="input-width" <el-input v-model="formData.title" clearable :placeholder="t('titlePlaceholder')" class="input-width"
maxlength="20" /> maxlength="20" />
@ -62,10 +63,11 @@
import { ref, reactive, computed, watch } from 'vue' import { ref, reactive, computed, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance, ElNotification } from 'element-plus' import type { FormInstance, ElNotification } from 'element-plus'
import { getArticleInfo, getArticleCategoryAll, addArticle, updateArticle } from '@/api/article' import { getArticleInfo, getArticleCategoryAll, addArticle, editArticle } from '@/api/article'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import useTabbarStore from '@/stores/modules/tabbar' import useTabbarStore from '@/stores/modules/tabbar'
import useAppStore from '@/stores/modules/app' import useAppStore from '@/stores/modules/app'
import { ElMessage } from 'element-plus'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
@ -77,7 +79,7 @@ const appStore = useAppStore()
// //
appStore.pageReturn = true; appStore.pageReturn = true;
watch(route, (newX,oldX) => { watch(route, (newX, oldX) => {
appStore.pageReturn = false; appStore.pageReturn = false;
}); });
@ -104,13 +106,20 @@ const formData: Record<string, any> = reactive({ ...initialFormData })
const setFormData = async (id: number = 0) => { const setFormData = async (id: number = 0) => {
loading.value = true; loading.value = true;
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
if(id){ if (id) {
const data = await (await getArticleInfo(id)).data const data = await (await getArticleInfo(id)).data
if (!data || Object.keys(data).length == 0) {
ElMessage.error(t('articleNull'))
setTimeout(() => {
router.go(-1);
}, 2000)
return false;
}
Object.keys(formData).forEach((key: string) => { Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key] if (data[key] != undefined) formData[key] = data[key]
}) })
loading.value = false; loading.value = false;
}else{ } else {
loading.value = false; loading.value = false;
} }
@ -139,9 +148,9 @@ const formRules = computed(() => {
{ {
validator: (rule: any, value: string, callback: any) => { validator: (rule: any, value: string, callback: any) => {
let content = value.replace(/<[^<>]+>/g, "").replace(/&nbsp;/gi, "") let content = value.replace(/<[^<>]+>/g, "").replace(/&nbsp;/gi, "")
if(!content && value.indexOf('img') === -1){ if (!content && value.indexOf('img') === -1) {
callback(new Error(t('contentPlaceholder'))) callback(new Error(t('contentPlaceholder')))
}else callback() } else callback()
}, },
trigger: ['blur', 'change'] trigger: ['blur', 'change']
} }
@ -155,7 +164,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
const data = formData const data = formData
const save = id ? updateArticle : addArticle const save = id ? editArticle : addArticle
save(data).then(res => { save(data).then(res => {
loading.value = false loading.value = false
back(); back();

View File

@ -14,8 +14,8 @@
<el-input v-model="articleTableData.searchParam.title" :placeholder="t('titlePlaceholder')" /> <el-input v-model="articleTableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
</el-form-item> </el-form-item>
<el-form-item :label="t('categoryName')" prop="category_id"> <el-form-item :label="t('categoryName')" prop="category_id">
<el-select v-model="articleTableData.searchParam.category_id" clearable :placeholder="t('categoryIdPlaceholder')" <el-select v-model="articleTableData.searchParam.category_id" clearable
class="input-width"> :placeholder="t('categoryIdPlaceholder')" class="input-width">
<el-option :label="t('selectPlaceholder')" value="" /> <el-option :label="t('selectPlaceholder')" value="" />
<el-option :label="item['name']" :value="item['category_id']" v-for="item in categoryList" /> <el-option :label="item['name']" :value="item['category_id']" v-for="item in categoryList" />
</el-select> </el-select>
@ -44,7 +44,7 @@
<el-table-column prop="category_name" :label="t('categoryName')" align="center" min-width="140" /> <el-table-column prop="category_name" :label="t('categoryName')" align="center" min-width="140" />
<el-table-column prop="summary" :label="t('summary')" width="180" :show-overflow-tooltip="true"/> <el-table-column prop="summary" :label="t('summary')" width="180" :show-overflow-tooltip="true" />
<el-table-column :label="t('isShow')" min-width="120" align="center"> <el-table-column :label="t('isShow')" min-width="120" align="center">
<template #default="{ row }"> <template #default="{ row }">
@ -83,8 +83,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getArticleList, deleteArticle,getArticleCategoryAll } from '@/api/article' import { getArticleList, deleteArticle, getArticleCategoryAll } from '@/api/article'
import { img } from '@/utils/common' import { img, getAppType } from '@/utils/common'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@ -96,7 +96,7 @@ const articleTableData = reactive({
data: [], data: [],
searchParam: { searchParam: {
title: '', title: '',
category_id:'' category_id: ''
} }
}) })
const categoryList = ref([]) const categoryList = ref([])

View File

@ -1,7 +1,7 @@
<template> <template>
<el-dialog v-model="showDialog" :title="popTitle" width="500px" <el-dialog v-model="showDialog" :title="popTitle" width="500px" :destroy-on-close="true">
:destroy-on-close="true"> <el-form :model="formData" label-width="90px" class="page-form" ref="formRef" :rules="formRules"
<el-form :model="formData" label-width="90px" class="page-form" ref="formRef" :rules="formRules" v-loading="loading"> v-loading="loading">
<el-form-item :label="t('menuName')" prop="menu_name"> <el-form-item :label="t('menuName')" prop="menu_name">
<el-input v-model="formData.menu_name" :placeholder="t('menuNamePlaceholder')" class="input-width" /> <el-input v-model="formData.menu_name" :placeholder="t('menuNamePlaceholder')" class="input-width" />
</el-form-item> </el-form-item>
@ -98,12 +98,12 @@ import { ref, reactive, computed } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import selectMenuItem from './select-menu-item.vue' import selectMenuItem from './select-menu-item.vue'
import { addMenu, updateMenu, getMenuInfo } from '@/api/sys' import { addMenu, editMenu, getMenuInfo } from '@/api/sys'
const showDialog = ref(false) const showDialog = ref(false)
const method = ref('post') const method = ref('post')
const loading = ref(false) const loading = ref(false)
let popTitle:string = ''; let popTitle: string = '';
/** /**
* 表单数据 * 表单数据
@ -121,8 +121,8 @@ const initialFormData = {
sort: '', sort: '',
status: 1, status: 1,
is_show: 1, is_show: 1,
menu_key:'', menu_key: '',
app_type:'' app_type: ''
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -149,7 +149,7 @@ const formRules = computed(() => {
{ required: true, message: t('menuKeyPlaceholder'), trigger: 'blur' }, { required: true, message: t('menuKeyPlaceholder'), trigger: 'blur' },
{ {
validator: (rule: any, value: string, callback: any) => { validator: (rule: any, value: string, callback: any) => {
if(!validataMenuKey(value)){ if (!validataMenuKey(value)) {
callback(new Error(t('menuKeyValidata'))) callback(new Error(t('menuKeyValidata')))
} }
@ -188,7 +188,7 @@ const emit = defineEmits(['complete'])
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
const save = formData.id ? updateMenu : addMenu const save = formData.id ? editMenu : addMenu
await formEl.validate(async (valid, fields) => { await formEl.validate(async (valid, fields) => {
if (valid) { if (valid) {
@ -219,7 +219,7 @@ const setFormData = async (row: any = null) => {
Object.keys(formData).forEach((key: string) => { Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key] if (data[key] != undefined) formData[key] = data[key]
}) })
}else{ } else {
Object.keys(formData).forEach((key: string) => { Object.keys(formData).forEach((key: string) => {
if (row[key] != undefined) formData[key] = row[key] if (row[key] != undefined) formData[key] = row[key]
}) })

View File

@ -1,7 +1,7 @@
<template> <template>
<el-dialog v-model="showDialog" :title="popTitle" width="500px" <el-dialog v-model="showDialog" :title="popTitle" width="500px" :destroy-on-close="true">
:destroy-on-close="true"> <el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form"
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading"> v-loading="loading">
<el-form-item :label="t('roleName')" prop="role_name"> <el-form-item :label="t('roleName')" prop="role_name">
<el-input v-model="formData.role_name" :placeholder="t('roleNamePlaceholder')" clearable <el-input v-model="formData.role_name" :placeholder="t('roleNamePlaceholder')" clearable
:disabled="formData.uid" class="input-width" maxlength="10" :show-word-limit="true" /> :disabled="formData.uid" class="input-width" maxlength="10" :show-word-limit="true" />
@ -42,12 +42,12 @@
import { ref, reactive, computed, watch, toRaw } from 'vue' import { ref, reactive, computed, watch, toRaw } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { addRole, updateRole, getRoleInfo, getSiteMenus } from '@/api/sys' import { addRole, editRole, getRoleInfo, getSiteMenus } from '@/api/sys'
import { debounce } from '@/utils/common' import { debounce } from '@/utils/common'
const showDialog = ref(false) const showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
let popTitle:string = ''; let popTitle: string = '';
// //
const menus = ref<Record<string, any>[]>([]) const menus = ref<Record<string, any>[]>([])
@ -110,7 +110,7 @@ const emit = defineEmits(['complete'])
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
const save = formData.role_id ? updateRole : addRole const save = formData.role_id ? editRole : addRole
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {

View File

@ -1,7 +1,7 @@
<template> <template>
<el-dialog v-model="showDialog" :title="popTitle" width="500px" <el-dialog v-model="showDialog" :title="popTitle" width="500px" :destroy-on-close="true">
:destroy-on-close="true"> <el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form"
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading"> v-loading="loading">
<el-form-item :label="t('accountNumber')" prop="username"> <el-form-item :label="t('accountNumber')" prop="username">
<el-input v-model="formData.username" :placeholder="t('accountNumberPlaceholder')" clearable <el-input v-model="formData.username" :placeholder="t('accountNumberPlaceholder')" clearable
:disabled="formData.uid" class="input-width" maxlength="10" show-word-limit /> :disabled="formData.uid" class="input-width" maxlength="10" show-word-limit />
@ -35,8 +35,8 @@
<el-form-item :label="t('status')"> <el-form-item :label="t('status')">
<el-radio-group v-model="formData.status"> <el-radio-group v-model="formData.status">
<el-radio :label="1">{{ t('statusNormal') }}</el-radio> <el-radio :label="1">{{ t('statusUnlock') }}</el-radio>
<el-radio :label="0">{{ t('statusDeactivate') }}</el-radio> <el-radio :label="0">{{ t('lock') }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -56,12 +56,12 @@
import { ref, reactive, computed } from 'vue' import { ref, reactive, computed } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { addUser, updateUser, getUserInfo } from '@/api/site' import { addUser, editUser, getUserInfo } from '@/api/site'
import { allRole } from '@/api/sys' import { allRole } from '@/api/sys'
const showDialog = ref(false) const showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
let popTitle:string = ''; let popTitle: string = '';
/** /**
* 表单数据 * 表单数据
@ -75,7 +75,7 @@ const initialFormData = {
confirm_password: '', confirm_password: '',
status: 1, status: 1,
role_ids: [], role_ids: [],
userrole:{} userrole: {}
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -115,6 +115,9 @@ const emit = defineEmits(['complete'])
const roles = ref<Record<string, any>>([]) const roles = ref<Record<string, any>>([])
allRole().then(res => { allRole().then(res => {
roles.value = res.data roles.value = res.data
roles.value.forEach(element => {
element.role_id = element.role_id.toString()
});
}) })
/** /**
@ -123,7 +126,7 @@ allRole().then(res => {
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
const save = formData.uid ? updateUser : addUser const save = formData.uid ? editUser : addUser
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {

View File

@ -31,9 +31,9 @@
</el-table-column> </el-table-column>
<el-table-column :label="t('status')" min-width="120" align="center"> <el-table-column :label="t('status')" min-width="120" align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-tag class="ml-2" type="success" v-if="row.status == 1">{{ t('statusNormal') }}</el-tag> <el-tag class="ml-2" type="success" v-if="row.status == 1">{{ t('statusUnlock') }}</el-tag>
<el-tag class="ml-2" type="error" v-if="row.status == 0">{{ <el-tag class="ml-2" type="error" v-if="row.status == 0">{{
t('statusDeactivate') t('statusLock')
}}</el-tag> }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>

View File

@ -36,21 +36,21 @@
<el-form-item :label="t('publicKey')"> <el-form-item :label="t('publicKey')">
<div class="input-width"> <div class="input-width">
<upload-file v-model="formData.public_key_crt" api="upload/cert/aliapp" /> <upload-file v-model="formData.public_key_crt" api="sys/document/aliyun" />
</div> </div>
<div class="form-tip">{{ t('publicKeyTips') }}</div> <div class="form-tip">{{ t('publicKeyTips') }}</div>
</el-form-item> </el-form-item>
<el-form-item :label="t('alipayPublicKey')"> <el-form-item :label="t('alipayPublicKey')">
<div class="input-width"> <div class="input-width">
<upload-file v-model="formData.alipay_public_key_crt" api="upload/cert/aliapp" /> <upload-file v-model="formData.alipay_public_key_crt" api="sys/document/aliyun" />
</div> </div>
<div class="form-tip">{{ t('alipayPublicKeyTips') }}</div> <div class="form-tip">{{ t('alipayPublicKeyTips') }}</div>
</el-form-item> </el-form-item>
<el-form-item :label="t('alipayWithCrt')"> <el-form-item :label="t('alipayWithCrt')">
<div class="input-width"> <div class="input-width">
<upload-file v-model="formData.alipay_with_crt" api="upload/cert/aliapp" /> <upload-file v-model="formData.alipay_with_crt" api="sys/document/aliyun" />
</div> </div>
<div class="form-tip">{{ t('alipayWithCrtTips') }}</div> <div class="form-tip">{{ t('alipayWithCrtTips') }}</div>
</el-form-item> </el-form-item>

View File

@ -18,6 +18,7 @@
<div class="cursor-pointer" @click="copyEvent(formData.request_url)">{{ t('copy') }}</div> <div class="cursor-pointer" @click="copyEvent(formData.request_url)">{{ t('copy') }}</div>
</template> </template>
</el-input> </el-input>
<span class="ml-2 cursor-pointer visit-btn" @click="visitFn">{{t('clickVisit')}}</span>
</el-form-item> </el-form-item>
</el-card> </el-card>
@ -35,7 +36,7 @@
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { setH5Config, getH5Config } from '@/api/h5' import { setH5Config, getH5Config } from '@/api/h5'
import { getSceneDomain } from '@/api/sys' import { getUrl } from '@/api/sys'
import { useClipboard } from '@vueuse/core' import { useClipboard } from '@vueuse/core'
import { ElMessage, FormInstance } from 'element-plus' import { ElMessage, FormInstance } from 'element-plus'
@ -60,8 +61,8 @@ getH5Config().then(res => {
/** /**
* 获取h5域名 * 获取h5域名
*/ */
getSceneDomain().then(res => { getUrl().then(res => {
formData.request_url = res.data.wap_domain formData.request_url = res.data.wap_url
}) })
@ -89,6 +90,11 @@ watch(copied, () => {
} }
}) })
// 访
const visitFn = ()=>{
window.open(formData.request_url);
}
/** /**
* 保存 * 保存
*/ */
@ -111,4 +117,8 @@ const save = async (formEl: FormInstance | undefined) => {
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped>
.visit-btn{
color:var(--el-color-primary);
}
</style>

View File

@ -1,12 +1,21 @@
<template> <template>
<div class="main-container"> <div class="main-container" v-loading="loading">
<el-form label-width="120px" ref="formRef" class="page-form"> <el-form :model="formData" label-width="120px" ref="formRef" class="page-form">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title">{{ t('pcInfo') }}</h3> <h3 class="panel-title">{{ t('pcInfo') }}</h3>
<el-form-item :label="t('preview')" prop="weapp_name"> <el-form-item :label="t('preview')" prop="weapp_name">
<img class="w-[1010px]" src="@/assets/images/channel/preview.png" alt=""> <img class="w-[500px]" src="@/assets/images/channel/preview.png" alt="">
</el-form-item>
<el-form-item :label="t('PCDomainName')">
<el-input :model-value="formData.request_url" class="input-width" :readonly="true">
<template #append>
<div class="cursor-pointer" @click="copyEvent(formData.request_url)">{{ t('copy') }}</div>
</template>
</el-input>
<span class="ml-2 cursor-pointer visit-btn" @click="visitFn">{{t('clickVisit')}}</span>
</el-form-item> </el-form-item>
</el-card> </el-card>
@ -15,9 +24,65 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getUrl } from '@/api/sys'
import { useClipboard } from '@vueuse/core' import { useClipboard } from '@vueuse/core'
import { ElMessage, FormInstance, FormRules } from 'element-plus' import { ElMessage, FormInstance, FormRules } from 'element-plus'
import { useRouter } from 'vue-router'
const loading = ref(true)
const formData = reactive<Record<string, string | boolean>>({
is_open: false,
request_url: ''
})
const formRef = ref<FormInstance>()
const router = useRouter()
/**
* 获取pc域名
*/
getUrl().then(res => {
formData.request_url = res.data.web_url;
loading.value = false;
})
/**
* 复制
*/
const { copy, isSupported, copied } = useClipboard()
const copyEvent = (text: string) => {
if (!isSupported.value) {
ElMessage({
message: t('notSupportCopy'),
type: 'warning'
})
return
}
copy(text)
}
watch(copied, () => {
if (copied.value) {
ElMessage({
message: t('copySuccess'),
type: 'success'
})
}
})
// 访
const visitFn = ()=>{
window.open(formData.request_url);
}
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped>
.visit-btn{
color:var(--el-color-primary);
}
</style>

View File

@ -3,7 +3,7 @@
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<el-alert class="warm-prompt" type="info"> <el-alert class="warm-prompt" type="info">
<template #default> <template #default>
<p class="text-base">{{t('operationTip')}} 1{{ t('operationTipOne') }}</p> <p class="text-base">{{ t('operationTip') }} 1{{ t('operationTipOne') }}</p>
<p class="text-base">2{{ t('operationTipTwo') }}</p> <p class="text-base">2{{ t('operationTipTwo') }}</p>
</template> </template>
</el-alert> </el-alert>
@ -51,7 +51,7 @@
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getTemplateList, getBatchAcquisition } from '@/api/weapp' import { getTemplateList, getBatchAcquisition } from '@/api/weapp'
import { updateMessageStatus } from '@/api/message' import { editMessageStatus } from '@/api/notice'
import { ElLoading } from 'element-plus' import { ElLoading } from 'element-plus'
const cronTableData = reactive({ const cronTableData = reactive({
@ -105,7 +105,7 @@ const infoSwitch = (res) => {
data.value.key = res.key data.value.key = res.key
data.value.type = 'weapp' data.value.type = 'weapp'
cronTableData.loading = true cronTableData.loading = true
updateMessageStatus(data.value).then(res => { editMessageStatus(data.value).then(res => {
loadCronList() loadCronList()
}).catch(() => { }).catch(() => {
cronTableData.loading = false cronTableData.loading = false
@ -115,13 +115,15 @@ const infoSwitch = (res) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep .warm-prompt{ ::v-deep .warm-prompt {
background-color: var(--el-color-primary-light-9); background-color: var(--el-color-primary-light-9);
} }
::v-deep .warm-prompt .el-icon{
::v-deep .warm-prompt .el-icon {
color: var(--el-color-primary); color: var(--el-color-primary);
} }
::v-deep .warm-prompt p{
::v-deep .warm-prompt p {
color: var(--el-color-primary); color: var(--el-color-primary);
} }
</style> </style>

View File

@ -129,7 +129,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getWechatConfig, getWechatStatic, updateWechatConfig } from '@/api/wechat' import { getWechatConfig, getWechatStatic, editWechatConfig } from '@/api/wechat'
import { useClipboard } from '@vueuse/core' import { useClipboard } from '@vueuse/core'
import { ElMessage, FormInstance, FormRules } from 'element-plus' import { ElMessage, FormInstance, FormRules } from 'element-plus'
@ -223,7 +223,7 @@ const save = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
updateWechatConfig(formData).then(() => { editWechatConfig(formData).then(() => {
loading.value = false loading.value = false
}).catch(() => { }).catch(() => {
loading.value = false loading.value = false

View File

@ -82,7 +82,7 @@
import { ref } from 'vue' import { ref } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { ElMessageBox, ElMessage } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus'
import { getWechatMenu, updateWechatMenu } from '@/api/wechat' import { getWechatMenu, editWechatMenu } from '@/api/wechat'
import menuForm from './components/menu-form.vue' import menuForm from './components/menu-form.vue'
const loading = ref(true) const loading = ref(true)
@ -199,7 +199,7 @@ const save = async () => {
} }
if (loading.value) return if (loading.value) return
loading.value = true loading.value = true
updateWechatMenu({ button: button.value }).then(() => { editWechatMenu({ button: button.value }).then(() => {
loading.value = false loading.value = false
}).catch(() => { }).catch(() => {
loading.value = false loading.value = false

View File

@ -3,7 +3,7 @@
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<el-alert class="warm-prompt" type="info"> <el-alert class="warm-prompt" type="info">
<template #default> <template #default>
<p class="text-base">{{t('operationTip')}} 1{{ t('operationTipOne') }}</p> <p class="text-base">{{ t('operationTip') }} 1{{ t('operationTipOne') }}</p>
<p class="text-base">2{{ t('operationTipTwo') }}</p> <p class="text-base">2{{ t('operationTipTwo') }}</p>
<p class="text-base">3{{ t('operationTipThree') }}</p> <p class="text-base">3{{ t('operationTipThree') }}</p>
<p class="text-base">4{{ t('operationTipFour') }}</p> <p class="text-base">4{{ t('operationTipFour') }}</p>
@ -58,7 +58,7 @@
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getTemplateList, getBatchAcquisition } from '@/api/wechat' import { getTemplateList, getBatchAcquisition } from '@/api/wechat'
import { updateMessageStatus } from '@/api/message' import { editMessageStatus } from '@/api/notice'
import { AnyObject } from '@/types/global' import { AnyObject } from '@/types/global'
import { ElLoading } from 'element-plus' import { ElLoading } from 'element-plus'
@ -114,7 +114,7 @@ const infoSwitch = (res: AnyObject) => {
data.value.key = res.key data.value.key = res.key
data.value.type = 'wechat' data.value.type = 'wechat'
cronTableData.loading = true cronTableData.loading = true
updateMessageStatus(data.value).then(res => { editMessageStatus(data.value).then(res => {
loadCronList() loadCronList()
}).catch(() => { }).catch(() => {
cronTableData.loading = false cronTableData.loading = false
@ -123,13 +123,15 @@ const infoSwitch = (res: AnyObject) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep .warm-prompt{ ::v-deep .warm-prompt {
background-color: var(--el-color-primary-light-9); background-color: var(--el-color-primary-light-9);
} }
::v-deep .warm-prompt .el-icon{
::v-deep .warm-prompt .el-icon {
color: var(--el-color-primary); color: var(--el-color-primary);
} }
::v-deep .warm-prompt p{
::v-deep .warm-prompt p {
color: var(--el-color-primary); color: var(--el-color-primary);
} }
</style> </style>

View File

@ -1,14 +1,15 @@
<template> <template>
<div class="main-container flex-1"> <div class="main-container flex-1">
<el-header class="flex items-center h-[60px] bg-primary px-[20px]"> <el-header class="flex items-center h-[60px] bg-primary px-[20px]">
<div class="text-white cursor-pointer flex items-center" @click="goBack"> <div class="text-white cursor-pointer flex items-center" @click="goBack">
<el-icon size="14"><ArrowLeft /></el-icon> <el-icon size="14">
<ArrowLeft />
</el-icon>
<span class="pl-[5px]">{{ t('back') }}</span> <span class="pl-[5px]">{{ t('back') }}</span>
</div> </div>
<div class="text-white ml-[10px] flex items-center"> <div class="text-white ml-[10px] flex items-center">
<span class="mr-[5px]"> {{ t('decorating') }}{{ diyStore.typeName }}</span> <span class="mr-[5px]"> {{ t('decorating') }}{{ diyStore.typeName }}</span>
<!-- <el-icon class="font-bold"><EditPen /></el-icon>--> <!-- <el-icon class="font-bold"><EditPen /></el-icon>-->
</div> </div>
<div class="flex-1"></div> <div class="flex-1"></div>
<el-button @click="save()">{{ t('save') }}</el-button> <el-button @click="save()">{{ t('save') }}</el-button>
@ -21,11 +22,13 @@
<!-- 组件列表区域 --> <!-- 组件列表区域 -->
<el-scrollbar class="px-[10px]"> <el-scrollbar class="px-[10px]">
<el-collapse v-model="activeNames" @change="handleChange"> <el-collapse v-model="activeNames" @change="handleChange">
<el-collapse-item v-for="(item,index) in component" :key="index" :title="item.type_name" :name="item.type"> <el-collapse-item v-for="(item, key) in component" :key="key" :title="item.title" :name="key">
<ul class="flex flex-row flex-wrap"> <ul class="flex flex-row flex-wrap">
<li v-for="(compItem,compIndex) in item.list" :key="compIndex" class="w-2/6 text-center cursor-pointer h-[75px]" :title="compItem.title" @click="diyStore.addComponent(compItem)"> <li v-for="(compItem, compKey) in item.list" :key="compKey"
<icon :name="compItem.icon" size="23px"/> class="w-2/6 text-center cursor-pointer h-[75px]" :title="compItem.title"
<span class="block text-base truncate">{{compItem.title}}</span> @click="diyStore.addComponent(compKey, compItem)">
<icon :name="compItem.icon" size="23px" />
<span class="block text-base truncate">{{ compItem.title }}</span>
</li> </li>
</ul> </ul>
</el-collapse-item> </el-collapse-item>
@ -37,33 +40,42 @@
<div class="preview-wrap flex-1 relative mt-[20px]"> <div class="preview-wrap flex-1 relative mt-[20px]">
<el-scrollbar> <el-scrollbar>
<el-button class="page-btn absolute right-[20px]" @click="diyStore.changeCurrentIndex(-99)">{{ t('pageSet')}}</el-button> <el-button class="page-btn absolute right-[20px]" @click="diyStore.changeCurrentIndex(-99)">{{
t('pageSet') }}</el-button>
<div class="diy-view-wrap w-[375px] shadow-lg mx-auto"> <div class="diy-view-wrap w-[375px] shadow-lg mx-auto">
<div class="preview-head bg-no-repeat bg-center bg-cover" @click="diyStore.changeCurrentIndex(-99)"> <div class="preview-head bg-no-repeat bg-center bg-cover" @click="diyStore.changeCurrentIndex(-99)">
<span class="text-base block text-center truncate cursor-pointer h-[64px] leading-[84px]">{{ diyStore.global.title }}</span> <span class="text-base block text-center truncate cursor-pointer h-[64px] leading-[84px]">{{
diyStore.global.title }}</span>
</div> </div>
<div class="preview-block relative"> <div class="preview-block relative">
<ul class="quick-action absolute text-center -right-[70px] top-[20px] w-[42px] rounded shadow-md"> <ul
class="quick-action absolute text-center -right-[70px] top-[20px] w-[42px] rounded shadow-md">
<el-tooltip effect="light" :content="t('moveUpComponent')" placement="right"> <el-tooltip effect="light" :content="t('moveUpComponent')" placement="right">
<icon name="iconfont-iconjiantoushang" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveUpComponent" /> <icon name="iconfont-iconjiantoushang" size="20px"
class="block cursor-pointer leading-[40px]" @click="diyStore.moveUpComponent" />
</el-tooltip> </el-tooltip>
<el-tooltip effect="light" :content="t('moveDownComponent')" placement="right"> <el-tooltip effect="light" :content="t('moveDownComponent')" placement="right">
<icon name="iconfont-iconjiantouxia" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveDownComponent" /> <icon name="iconfont-iconjiantouxia" size="20px"
class="block cursor-pointer leading-[40px]" @click="diyStore.moveDownComponent" />
</el-tooltip> </el-tooltip>
<el-tooltip effect="light" :content="t('copyComponent')" placement="right"> <el-tooltip effect="light" :content="t('copyComponent')" placement="right">
<icon name="iconfont-iconcopy-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.copyComponent" /> <icon name="iconfont-iconcopy-line" size="20px"
class="block cursor-pointer leading-[40px]" @click="diyStore.copyComponent" />
</el-tooltip> </el-tooltip>
<el-tooltip effect="light" :content="t('delComponent')" placement="right"> <el-tooltip effect="light" :content="t('delComponent')" placement="right">
<icon name="iconfont-icondelete-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.delComponent"/> <icon name="iconfont-icondelete-line" size="20px"
class="block cursor-pointer leading-[40px]" @click="diyStore.delComponent" />
</el-tooltip> </el-tooltip>
<el-tooltip effect="light" :content="t('resetComponent')" placement="right"> <el-tooltip effect="light" :content="t('resetComponent')" placement="right">
<icon name="iconfont-iconloader-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.resetComponent" /> <icon name="iconfont-iconloader-line" size="20px"
class="block cursor-pointer leading-[40px]" @click="diyStore.resetComponent" />
</el-tooltip> </el-tooltip>
</ul> </ul>
<!-- 组件预览渲染区域 --> <!-- 组件预览渲染区域 -->
<iframe id="previewIframe" v-show="wapDomain" :src="wapDomain" frameborder="0" class="preview-iframe w-[375px]"></iframe> <iframe id="previewIframe" v-show="wapDomain" :src="wapDomain" frameborder="0"
class="preview-iframe w-[375px]"></iframe>
</div> </div>
</div> </div>
@ -78,10 +90,12 @@
<el-card class="box-card" shadow="never"> <el-card class="box-card" shadow="never">
<template #header> <template #header>
<div class="card-header flex justify-between items-center"> <div class="card-header flex justify-between items-center">
<span class="title flex-1">{{ diyStore.currentIndex == -99 ? t('pageSet') : diyStore.editComponent.componentTitle }}</span> <span class="title flex-1">{{ diyStore.currentIndex == -99 ? t('pageSet') :
diyStore.editComponent.componentTitle }}</span>
</div> </div>
</template> </template>
<component :is="modules[diyStore.currentComponent]" :value="diyStore.value[diyStore.currentIndex]" /> <component :is="modules[diyStore.currentComponent]"
:value="diyStore.value[diyStore.currentIndex]" />
</el-card> </el-card>
</el-scrollbar> </el-scrollbar>
@ -92,34 +106,33 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {ref, reactive, toRaw, onMounted, watch} from 'vue' import { ref, reactive, toRaw, onMounted, watch } from 'vue'
import {t} from '@/lang' import { t } from '@/lang'
import { addDiyPage, updateDiyPage,initPage} from '@/api/diy'; import { addDiyPage, editDiyPage, initPage } from '@/api/diy';
import {useRoute,useRouter} from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { cloneDeep, range, isEmpty } from 'lodash-es' import { cloneDeep, range, isEmpty } from 'lodash-es'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import useDiyStore from '@/stores/modules/diy' import useDiyStore from '@/stores/modules/diy'
import { getWapDomain } from '@/utils/common'
const diyStore = useDiyStore() const diyStore = useDiyStore()
const route = useRoute(); const route = useRoute();
const router = useRouter() const router = useRouter()
const wapDomain = ref('') const wapDomain = ref('')
const component = ref([]) const component = ref([])
const componentType:string[] = reactive([]) const componentType: string[] = reactive([])
const activeNames = ref(componentType) const activeNames = ref(componentType)
const handleChange = (val: string[]) => {} const handleChange = (val: string[]) => { }
route.query.id = route.query.id || 0; route.query.id = route.query.id || 0;
route.query.name = route.query.name || ''; route.query.name = route.query.name || '';
route.query.type = route.query.type || ''; // route.query.type = route.query.type || ''; //
route.query.title = route.query.title || ''; route.query.title = route.query.title || '';
// //
const originData = reactive({ const originData = reactive({
id: diyStore.id, id: diyStore.id,
name: diyStore.name, name: diyStore.name,
title: diyStore.global.title, title: diyStore.global.title,
@ -127,14 +140,14 @@
global: toRaw(diyStore.global), global: toRaw(diyStore.global),
value: toRaw(diyStore.value) value: toRaw(diyStore.value)
}) })
}) })
// //
const isChange = ref(true) // truefalse const isChange = ref(true) // truefalse
const goBack = () => { const goBack = () => {
if(isChange.value){ if (isChange.value) {
router.push('/diy/list'); router.push('/diy/list');
}else{ } else {
// //
ElMessageBox.confirm( ElMessageBox.confirm(
t('leavePageTitleTips'), t('leavePageTitleTips'),
@ -150,19 +163,19 @@
}).catch(() => { }).catch(() => {
}) })
} }
} }
// //
const modulesFiles = import.meta.glob('./components/*.vue', {eager: true}) const modulesFiles = import.meta.glob('./components/*.vue', { eager: true })
const modules = {} const modules = {}
for (const [key, value] of Object.entries(modulesFiles)) { for (const [key, value] of Object.entries(modulesFiles)) {
const moduleName = key.replace(/^\.\/(.*)\.\w+$/, '$1') const moduleName = key.replace(/^\.\/(.*)\.\w+$/, '$1')
const name = moduleName.split('/')[1] const name = moduleName.split('/')[1]
modules[name] = value.default modules[name] = value.default
} }
// //
watch( watch(
() => diyStore, () => diyStore,
(newValue, oldValue) => { (newValue, oldValue) => {
let data = { let data = {
@ -179,16 +192,15 @@
isChange.value = JSON.stringify(data) == JSON.stringify(originData); isChange.value = JSON.stringify(data) == JSON.stringify(originData);
}, },
{ deep: true } { deep: true }
) )
// //
initPage({ initPage({
id:route.query.id, id: route.query.id,
name:route.query.name, name: route.query.name,
type:route.query.type, type: route.query.type,
title:route.query.title title: route.query.title
}).then(res=> { }).then(res => {
if (res.code == 200) {
let data = res.data; let data = res.data;
diyStore.id = data.id || 0; diyStore.id = data.id || 0;
diyStore.name = data.name; diyStore.name = data.name;
@ -197,7 +209,7 @@
if (data.value) { if (data.value) {
let sources = JSON.parse(data.value); let sources = JSON.parse(data.value);
diyStore.global = sources.global; diyStore.global = sources.global;
if(sources.value.length) { if (sources.value.length) {
diyStore.value = sources.value; diyStore.value = sources.value;
// diyStore.changeCurrentIndex(0,diyStore.value[0]); // diyStore.changeCurrentIndex(0,diyStore.value[0]);
} }
@ -215,12 +227,12 @@
}); });
component.value = data.component component.value = data.component
component.value.forEach((item: any) => { for (let type in component.value) {
componentType.push(item.type) componentType.push(type)
item.list.forEach((comp: any) => { for (let key in component.value[type].list) {
let com = cloneDeep(comp); let com = cloneDeep(component.value[type].list[key]);
com.id = diyStore.generateRandom(); com.id = diyStore.generateRandom();
com.componentName = com.name; com.componentName = key;
com.componentTitle = com.title; com.componentTitle = com.title;
com.maxCount = com.max_count; com.maxCount = com.max_count;
Object.assign(com, com.value); Object.assign(com, com.value);
@ -231,20 +243,19 @@
delete com.icon; delete com.icon;
delete com.max_count; delete com.max_count;
diyStore.components.push(com) diyStore.components.push(com)
})
})
wapDomain.value = `${getWapDomain()}/${data.page}?mode=decorate`; // decorate 访
} }
}) }
onMounted(() => { wapDomain.value = `${data.domain_url.wap_url}/${data.page}?mode=decorate`; // decorate 访
})
onMounted(() => {
// uniapp iframe // uniapp iframe
window.previewIframe = document.getElementById('previewIframe') window.previewIframe = document.getElementById('previewIframe')
}) })
// uni-app // uni-app
window.addEventListener('message', (event) => { window.addEventListener('message', (event) => {
try { try {
let data = JSON.parse(event.data); let data = JSON.parse(event.data);
if (!data.type) return; if (!data.type) return;
@ -267,12 +278,12 @@
break; break;
} }
} catch (e) { } catch (e) {
console.log('后台接受数据错误',e) console.log('后台接受数据错误', e)
} }
}, false); }, false);
const loading = ref(false) const loading = ref(false)
const save = ()=> { const save = () => {
if (!diyStore.verify()) { if (!diyStore.verify()) {
return; return;
} }
@ -291,7 +302,7 @@
}) })
}; };
const save = diyStore.id ? updateDiyPage : addDiyPage const save = diyStore.id ? editDiyPage : addDiyPage
save(data).then((res: any) => { save(data).then((res: any) => {
loading.value = false loading.value = false
if (res.code == 200) { if (res.code == 200) {
@ -305,61 +316,59 @@
loading.value = false loading.value = false
}) })
} }
</script> </script>
<style lang="scss"> <style lang="scss">
.el-collapse-item__wrap { .el-collapse-item__wrap {
border-bottom: none; border-bottom: none;
} }
.el-collapse-item__content { .el-collapse-item__content {
padding-bottom: 0; padding-bottom: 0;
} }
.el-collapse-item__header { .el-collapse-item__header {
font-size: var(--el-font-size-base); font-size: var(--el-font-size-base);
} }
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
.full-container { .full-container {
height: calc(100vh - 60px); height: calc(100vh - 60px);
} }
.preview-iframe{ .preview-iframe {
height: calc(100vh - 160px); height: calc(100vh - 160px);
} }
.component-list { .component-list {
background: var(--el-bg-color); background: var(--el-bg-color);
} }
.component-list ul li { .component-list ul li {
&:not(.disabled):hover { &:not(.disabled):hover {
color: var(--el-color-primary); color: var(--el-color-primary);
background: var(--el-color-primary-light-9); background: var(--el-color-primary-light-9);
} }
} }
.diy-view-wrap { .diy-view-wrap {
background: var(--el-bg-color-page); background: var(--el-bg-color-page);
} }
.diy-view-wrap .preview-head { .diy-view-wrap .preview-head {
background-image: url(assets/images/diy_preview_head.png); background-image: url(assets/images/diy_preview_head.png);
background-color: var(--el-bg-color); background-color: var(--el-bg-color);
} }
.quick-action { .quick-action {
background: var(--el-bg-color); background: var(--el-bg-color);
} }
.edit-attribute-wrap { .edit-attribute-wrap {
background: var(--el-bg-color); background: var(--el-bg-color);
} }
.edit-attribute-wrap .box-card { .edit-attribute-wrap .box-card {
border: none; border: none;
} }</style>
</style>

View File

@ -49,18 +49,21 @@
import {t} from '@/lang' import {t} from '@/lang'
import {useRoute, useRouter} from 'vue-router' import {useRoute, useRouter} from 'vue-router'
import {getWeappConfig} from '@/api/weapp' import {getWeappConfig} from '@/api/weapp'
import {getUrl} from '@/api/sys'
import {useClipboard} from '@vueuse/core' import {useClipboard} from '@vueuse/core'
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import {img} from '@/utils/common' import {img} from '@/utils/common'
import {getWapDomain} from '@/utils/common'
import QRCode from "qrcode"; import QRCode from "qrcode";
const wapDomain = ref(getWapDomain() + '/pages/index/index') const wapDomain = ref('')
const wapImage = ref('') const wapImage = ref('')
getUrl().then((res:any)=>{
wapDomain.value = res.data.wap_url + '/pages/index/index'
QRCode.toDataURL(wapDomain.value, {errorCorrectionLevel: 'L', margin: 0, width: 100}).then(url => { QRCode.toDataURL(wapDomain.value, {errorCorrectionLevel: 'L', margin: 0, width: 100}).then(url => {
wapImage.value = url wapImage.value = url
}) })
});
const router = useRouter() const router = useRouter()
@ -84,6 +87,7 @@
path: '/decorate/edit', path: '/decorate/edit',
query: {name: 'DIY_INDEX'} query: {name: 'DIY_INDEX'}
}); });
const toDecorate = () => { const toDecorate = () => {
router.push('/decorate/edit?name=DIY_INDEX') router.push('/decorate/edit?name=DIY_INDEX')
// window.open(url.href); // window.open(url.href);

View File

@ -2,29 +2,21 @@
<div class="main-container"> <div class="main-container">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<div class="flex"> <div class="flex">
<el-button type="primary" @click="dialogVisible=true"> <el-button type="primary" @click="dialogVisible = true">
{{ t('addDiyPage') }} {{ t('addDiyPage') }}
</el-button> </el-button>
</div> </div>
<el-card class="box-card !border-none my-[16px] table-search-wrap" shadow="never"> <el-card class="box-card !border-none my-[16px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="diyRouteTableData.searchParam" ref="searchFormDiyRouteRef" v-if="tabValue == 'route'"> <el-form :inline="true" :model="diyPageTableData.searchParam" ref="searchFormDiyPageRef"
v-if="tabValue == 'diy'">
<el-form-item :label="t('title')" prop="title"> <el-form-item :label="t('title')" prop="title">
<el-input v-model="diyRouteTableData.searchParam.title" :placeholder="t('titlePlaceholder')"/> <el-input v-model="diyPageTableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadDiyRouteList()">{{ t('search') }}</el-button>
<el-button @click="searchFormDiyRouteRef?.resetFields()">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-form :inline="true" :model="diyPageTableData.searchParam" ref="searchFormDiyPageRef" v-if="tabValue == 'diy'">
<el-form-item :label="t('title')" prop="title">
<el-input v-model="diyPageTableData.searchParam.title" :placeholder="t('titlePlaceholder')"/>
</el-form-item> </el-form-item>
<el-form-item :label="t('typeName')" prop="type"> <el-form-item :label="t('typeName')" prop="type">
<el-select v-model="diyPageTableData.searchParam.type" :placeholder="t('pageTypePlaceholder')"> <el-select v-model="diyPageTableData.searchParam.type" :placeholder="t('pageTypePlaceholder')">
<el-option :label="t('all')" value="" /> <el-option :label="t('all')" value="" />
<el-option v-for="item in pageType" :label="item.type_name" :value="item.type" /> <el-option v-for="(item, key) in pageType" :label="item.title" :value="key" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -32,6 +24,16 @@
<el-button @click="searchFormDiyPageRef?.resetFields()">{{ t('reset') }}</el-button> <el-button @click="searchFormDiyPageRef?.resetFields()">{{ t('reset') }}</el-button>
</el-form-item> </el-form-item>
</el-form> </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="searchFormDiyRouteRef?.resetFields()">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card> </el-card>
<div class="mt-[16px]"> <div class="mt-[16px]">
@ -45,9 +47,9 @@
<span>{{ !diyPageTableData.loading ? t('emptyData') : '' }}</span> <span>{{ !diyPageTableData.loading ? t('emptyData') : '' }}</span>
</template> </template>
<el-table-column prop="title" :label="t('title')" 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 prop="type_name" :label="t('typeName')" min-width="80" />
<el-table-column :label="t('status')" min-width="120"> <el-table-column :label="t('status')" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
<span v-if="row.type == 'DIY_PAGE'">-</span> <span v-if="row.type == 'DIY_PAGE'">-</span>
<template v-else> <template v-else>
@ -56,21 +58,27 @@
</template> </template>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="update_time" :label="t('updateTime')" min-width="120"/> <el-table-column prop="update_time" :label="t('updateTime')" min-width="120" />
<el-table-column :label="t('operation')" fixed="right" min-width="120"> <el-table-column :label="t('operation')" fixed="right" align="right" min-width="160">
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="promoteEvent(row)">{{ t('promote') }}</el-button> <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' && row.is_default == 0" type="primary" link
<el-button v-if="row.type == 'DIY_PAGE'" type="primary" link @click="openShare(row)">{{ t('shareSet') }}</el-button> @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 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 v-if="row.type == 'DIY_PAGE' || row.is_default == 0" type="danger" link
@click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="mt-[16px] flex justify-end"> <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"/> <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> </div>
</el-tab-pane> </el-tab-pane>
<!-- 基础页面路径 --> <!-- 基础页面路径 -->
@ -79,11 +87,12 @@
<template #empty> <template #empty>
<span>{{ !diyRouteTableData.loading ? t('emptyData') : '' }}</span> <span>{{ !diyRouteTableData.loading ? t('emptyData') : '' }}</span>
</template> </template>
<el-table-column prop="title" :label="t('title')" min-width="120"/> <el-table-column prop="title" :label="t('title')" min-width="120" />
<el-table-column prop="page" :label="t('wapUrl')" min-width="120"> <el-table-column prop="page" :label="t('wapUrl')" min-width="120">
<template #default="{ row }"> <template #default="{ row }">
<span class="mr-[10px]">{{ getWapDomain() + row.page }}</span> <span class="mr-[10px]">{{ wapDomain + row.page }}</span>
<el-button type="primary" link @click="copyEvent(getWapDomain() + row.page)">{{ t('copy') }}</el-button> <el-button type="primary" link @click="copyEvent(wapDomain + row.page)">{{ t('copy')
}}</el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="page" :label="t('weappUrl')" min-width="120"> <el-table-column prop="page" :label="t('weappUrl')" min-width="120">
@ -94,13 +103,11 @@
</el-table-column> </el-table-column>
<el-table-column :label="t('share')" fixed="right" min-width="80"> <el-table-column :label="t('share')" fixed="right" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
<el-button v-if="row.is_share == 1" type="primary" link @click="openShare(row)">{{ t('shareSet') }}</el-button> <el-button v-if="row.is_share == 1" type="primary" link @click="openShare(row)">{{
t('shareSet') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="diyRouteTableData.page" v-model:page-size="diyRouteTableData.limit" layout="total, sizes, prev, pager, next, jumper" :total="diyRouteTableData.total" @size-change="loadDiyPageList()" @current-change="loadDiyPageList"/>
</div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -115,17 +122,18 @@
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules"> <el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules">
<el-form-item :label="t('typeName')" prop="type"> <el-form-item :label="t('typeName')" prop="type">
<el-select v-model="formData.type" :placeholder="t('pageTypePlaceholder')"> <el-select v-model="formData.type" :placeholder="t('pageTypePlaceholder')">
<el-option v-for="item in pageType" :label="item.type_name" :value="item.type" /> <el-option v-for="(item, key) in pageType" :label="item.title" :value="key" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('title')" prop="title"> <el-form-item :label="t('title')" prop="title">
<el-input v-model="formData.title" :placeholder="t('titlePlaceholder')" clearable maxlength="12" show-word-limit/> <el-input v-model="formData.title" :placeholder="t('titlePlaceholder')" clearable maxlength="12"
show-word-limit />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="dialogVisible = false">{{ t('cancel')}}</el-button> <el-button @click="dialogVisible = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="addEvent(formRef)">{{ t('confirm') }}</el-button> <el-button type="primary" @click="addEvent(formRef)">{{ t('confirm') }}</el-button>
</span> </span>
</template> </template>
@ -143,19 +151,21 @@
<span>{{ sharePage }}</span> <span>{{ sharePage }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="t('shareTitle')" prop="title"> <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-input v-model="shareFormData[tabShareType].title" :placeholder="t('shareTitlePlaceholder')"
clearable maxlength="30" show-word-limit />
</el-form-item> </el-form-item>
<el-form-item :label="t('shareDesc')" prop="desc" v-if="tabShareType == 'wechat'"> <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-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>
<el-form-item :label="t('shareImageUrl')" prop="url"> <el-form-item :label="t('shareImageUrl')" prop="url">
<upload-image v-model="shareFormData[tabShareType].url" :limit="1"/> <upload-image v-model="shareFormData[tabShareType].url" :limit="1" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="shareDialogVisible = false">{{ t('cancel')}}</el-button> <el-button @click="shareDialogVisible = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="shareEvent(shareFormRef)">{{ t('confirm') }}</el-button> <el-button type="primary" @click="shareEvent(shareFormRef)">{{ t('confirm') }}</el-button>
</span> </span>
</template> </template>
@ -165,21 +175,22 @@
<el-dialog v-model="promoteDialogVisible" :title="t('promote')" width="30%"> <el-dialog v-model="promoteDialogVisible" :title="t('promote')" width="30%">
<el-form label-width="90px"> <el-form label-width="90px">
<el-form-item :label="t('shareLink')"> <el-form-item :label="t('shareLink')">
<el-input readonly :value="wapDomain"> <el-input readonly :value="promoteWapDomain">
<template #append> <template #append>
<el-button @click="copyEvent(wapDomain)" class="bg-primary copy">{{ t('copy') }}</el-button> <el-button @click="copyEvent(promoteWapDomain)" class="bg-primary copy">{{ t('copy')
}}</el-button>
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item label=" "> <el-form-item label=" ">
<el-image :src="wapImage"/> <el-image :src="wapImage" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- <el-tabs v-model="tabPromote">--> <!-- <el-tabs v-model="tabPromote">-->
<!-- <el-tab-pane :label="t('wechat')" name="wechat"></el-tab-pane>--> <!-- <el-tab-pane :label="t('wechat')" name="wechat"></el-tab-pane>-->
<!-- <el-tab-pane :label="t('weapp')" name="weapp"></el-tab-pane>--> <!-- <el-tab-pane :label="t('weapp')" name="weapp"></el-tab-pane>-->
<!-- </el-tabs>--> <!-- </el-tabs>-->
</el-dialog> </el-dialog>
@ -187,26 +198,27 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {reactive, ref,watch,computed} from 'vue' import { reactive, ref, watch, computed } from 'vue'
import {t} from '@/lang' import { t } from '@/lang'
import {getDiyPageList, deleteDiyPage, setUseDiyPage,getDiyPageType,getDiyRouteList,updateDiyPageShare,updateDiyRouteShare} from '@/api/diy' import { getDiyPageList, deleteDiyPage, setUseDiyPage, getDiyPageType, getDiyRouteList, getDiyRouteInfo, editDiyPageShare, editDiyRouteShare } from '@/api/diy'
import {TabsPaneContext, ElMessage,ElMessageBox,FormInstance} from 'element-plus' import { TabsPaneContext, ElMessage, ElMessageBox, FormInstance } from 'element-plus'
import {useRouter} from 'vue-router' import { useRouter } from 'vue-router'
import {useClipboard} from '@vueuse/core' import { useClipboard } from '@vueuse/core'
import { getWapDomain } from '@/utils/common' import QRCode from "qrcode";
import QRCode from "qrcode"; import { getUrl } from '@/api/sys'
const pageType:any = reactive([]) const pageType: any = reactive({})
const router = useRouter() const router = useRouter()
// //
const formData = reactive({ const formData = reactive({
type: '', type: '',
title: '' title: ''
}) })
//
const formRules = computed(() => { //
const formRules = computed(() => {
return { return {
type: [ type: [
{ required: true, message: t('pageTypePlaceholder'), trigger: 'blur' }, { required: true, message: t('pageTypePlaceholder'), trigger: 'blur' },
@ -215,10 +227,11 @@
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' }, { required: true, message: t('titlePlaceholder'), trigger: 'blur' },
] ]
} }
}) })
const formRef = ref<FormInstance>()
const dialogVisible = ref(false) const formRef = ref<FormInstance>()
const addEvent = async (formEl: FormInstance | undefined) => { const dialogVisible = ref(false)
const addEvent = async (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
@ -227,48 +240,47 @@
router.push('/decorate/edit?type=' + formData.type + '&title=' + formData.title); router.push('/decorate/edit?type=' + formData.type + '&title=' + formData.title);
} }
}) })
} }
let diyRouteTableData = reactive({ let diyRouteTableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam:{ searchParam: {
"title":"", "title": "",
} }
}) })
/** const wapDomain = ref('')
const getDomain = async () => {
wapDomain.value = (await getUrl()).data.wap_url;
};
getDomain();
/**
* 获取自定义路由列表 * 获取自定义路由列表
*/ */
const loadDiyRouteList = (page: number = 1) => { const loadDiyRouteList = () => {
diyRouteTableData.loading = true diyRouteTableData.loading = true
diyRouteTableData.page = page
getDiyRouteList({ getDiyRouteList({
page: diyRouteTableData.page,
limit: diyRouteTableData.limit,
...diyRouteTableData.searchParam ...diyRouteTableData.searchParam
}).then(res => { }).then(res => {
diyRouteTableData.loading = false diyRouteTableData.loading = false
diyRouteTableData.data = res.data.data diyRouteTableData.data = res.data
diyRouteTableData.total = res.data.total
}).catch(() => { }).catch(() => {
diyRouteTableData.loading = false diyRouteTableData.loading = false
}) })
} }
loadDiyRouteList() loadDiyRouteList()
// //
getDiyPageType({}).then(res=>{ getDiyPageType({}).then(res => {
for (let item in res.data){ for (let key in res.data) {
pageType.push(res.data[item]) pageType[key] = res.data[key]
} }
}) })
let diyPageTableData:any = reactive({ let diyPageTableData: any = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
@ -278,25 +290,24 @@
"title": "", "title": "",
"type": '', "type": '',
} }
}) })
const tabValue = ref('diy') const tabValue = ref('diy')
const handleClick = (tab: TabsPaneContext, event: Event) => { const handleClick = (tab: TabsPaneContext, event: Event) => {
tabValue.value = tab.props.name; tabValue.value = tab.props.name;
if(tabValue.value == 'diy'){ if (tabValue.value == 'diy') {
loadDiyPageList() loadDiyPageList()
}else{ } else {
loadDiyRouteList() loadDiyRouteList()
} }
} }
const searchFormDiyRouteRef = ref<FormInstance>() const searchFormDiyRouteRef = ref<FormInstance>()
const searchFormDiyPageRef = ref<FormInstance>()
const searchFormDiyPageRef = ref<FormInstance>() //
const loadDiyPageList = (page: number = 1) => {
//
const loadDiyPageList = (page: number = 1) => {
diyPageTableData.loading = true diyPageTableData.loading = true
diyPageTableData.page = page diyPageTableData.page = page
@ -311,21 +322,21 @@
}).catch(() => { }).catch(() => {
diyPageTableData.loading = false diyPageTableData.loading = false
}) })
} }
loadDiyPageList() loadDiyPageList()
// //
const editEvent = (data: any) => { const editEvent = (data: any) => {
let url = router.resolve({ let url = router.resolve({
path: '/decorate/edit', path: '/decorate/edit',
query: {id: data.id} query: { id: data.id }
}); });
window.open(url.href); window.open(url.href);
} }
// //
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('diyPageDeleteTips'), t('warning'), ElMessageBox.confirm(t('diyPageDeleteTips'), t('warning'),
{ {
confirmButtonText: t('confirm'), confirmButtonText: t('confirm'),
@ -338,20 +349,20 @@
}).catch(() => { }).catch(() => {
}) })
}) })
} }
// 使 // 使
const setUse = (data: any) => { const setUse = (data: any) => {
setUseDiyPage({id: data.id}).then(() => { setUseDiyPage({ id: data.id }).then(() => {
loadDiyPageList() loadDiyPageList()
}) })
} }
/** /**
* 复制 * 复制
*/ */
const {copy, isSupported, copied} = useClipboard() const { copy, isSupported, copied } = useClipboard()
const copyEvent = (text: string) => { const copyEvent = (text: string) => {
if (!isSupported.value) { if (!isSupported.value) {
ElMessage({ ElMessage({
message: t('notSupportCopy'), message: t('notSupportCopy'),
@ -359,33 +370,40 @@
}) })
} }
copy(text) copy(text)
} }
watch(copied, () => { watch(copied, () => {
if (copied.value) { if (copied.value) {
ElMessage({ ElMessage({
message: t('copySuccess'), message: t('copySuccess'),
type: 'success' type: 'success'
}) })
} }
}) })
const tabShareType = ref('wechat') const tabShareType = ref('wechat')
const sharePage = ref('') const sharePage = ref('')
const shareFormId = ref(0) const shareFormId = ref(0)
const shareFormData = reactive({ const diyRouteData = reactive({
wechat:{ title: '',
name: '',
page: '',
is_share: 0,
sort: 0
})
const shareFormData = reactive({
wechat: {
title: '', title: '',
desc: '', desc: '',
url: '' url: ''
}, },
weapp:{ weapp: {
title: '', title: '',
url: '' url: ''
} }
}) })
const shareDialogVisible = ref(false) const shareDialogVisible = ref(false)
const shareFormRules = computed(() => { const shareFormRules = computed(() => {
return { return {
// title: [ // title: [
// { required: true, message: t('shareTitlePlaceholder'), trigger: 'blur' }, // { required: true, message: t('shareTitlePlaceholder'), trigger: 'blur' },
@ -401,15 +419,38 @@
// // } // // }
// ], // ],
} }
}) })
const shareFormRef = ref<FormInstance>() const shareFormRef = ref<FormInstance>()
const openShare = (row:any)=> { 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
}
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; shareFormId.value = row.id;
sharePage.value = row.title; sharePage.value = row.title;
let share = row.share ? JSON.parse(row.share) : { let share = row.share ? JSON.parse(row.share) : {
wechat: {title: '', desc: '', url: ''}, wechat: { title: '', desc: '', url: '' },
weapp: {title: '', url: ''} weapp: { title: '', url: '' }
}; };
if (share) { if (share) {
shareFormData.wechat = share.wechat; shareFormData.wechat = share.wechat;
@ -417,21 +458,22 @@
} }
shareDialogVisible.value = true; shareDialogVisible.value = true;
} }
const shareEvent = async (formEl: FormInstance | undefined) => { const shareEvent = async (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
let save = tabValue.value == 'diy' ? updateDiyPageShare : updateDiyRouteShare let save = tabValue.value == 'diy' ? editDiyPageShare : editDiyRouteShare
save({ save({
id:shareFormId.value, id: shareFormId.value,
share:JSON.stringify(shareFormData) share: JSON.stringify(shareFormData),
...diyRouteData
}).then(() => { }).then(() => {
if(tabValue.value == 'diy'){ if (tabValue.value == 'diy') {
loadDiyPageList() loadDiyPageList()
}else{ } else {
loadDiyRouteList() loadDiyRouteList()
} }
shareDialogVisible.value = false; shareDialogVisible.value = false;
@ -439,27 +481,27 @@
}) })
} }
}) })
} }
const promoteDialogVisible = ref(false) const promoteDialogVisible = ref(false)
const tabPromote = ref('wechat') const tabPromote = ref('wechat')
const wapDomain = ref('') const promoteWapDomain = ref('')
const wapImage = ref('') const wapImage = ref('')
const promoteEvent = (data:any)=>{ const promoteEvent = (data: any) => {
wapDomain.value = getWapDomain() + '/pages/index/diy?id=' + data.id; promoteWapDomain.value = wapDomain.value + '/pages/index/diy?id=' + data.id;
QRCode.toDataURL(wapDomain.value, {errorCorrectionLevel: 'L', margin: 0, width: 100}).then(url => { QRCode.toDataURL(promoteWapDomain.value, { errorCorrectionLevel: 'L', margin: 0, width: 100 }).then(url => {
wapImage.value = url wapImage.value = url
}) })
promoteDialogVisible.value = true; promoteDialogVisible.value = true;
console.log('promoteEvent',data) console.log('promoteEvent', data)
} }
</script> </script>
<style lang="scss"> <style lang="scss">
.copy { .copy {
background: var(--el-color-primary) !important; background: var(--el-color-primary) !important;
color: var(--el-color-white) !important; color: var(--el-color-white) !important;
} }
</style> </style>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -5,9 +5,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import {ref} from 'vue' import {ref} from 'vue'
import {useRouter} from 'vue-router' import {useRouter} from 'vue-router'
import {getWapDomain} from '@/utils/common'
const wapDomain = ref(getWapDomain() + '/pages/member/index')
const router = useRouter() const router = useRouter()
const url = router.resolve({ const url = router.resolve({

View File

@ -1,16 +0,0 @@
<template>
<div class="error404">
<error code="403" :title="t('tips')" :show-btn="false">
<template #content>
<div class="flex justify-center">
<img class="w-[150px] h-[150px]" src="@/assets/images/no_perms.png" alt="" />
</div>
</template>
</error>
</div>
</template>
<script lang="ts" setup>
import error from './components/error.vue'
import { t } from '@/lang'
</script>

View File

@ -1,9 +1,56 @@
<template> <template>
<div class="error404"> <div class="error">
<error code="404" title="哎呀,出错了!您访问的页面不存在…"></error> <div>
<slot name="content">
<div class="error-code">404</div>
</slot>
<div class="text-lg text-tx-secondary mt-7 mb-7">页面不存在</div>
<div>
<el-button type="primary" @click="router.go(-1)">
{{ second }} 秒后返回上一页
</el-button>
</div>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import Error from './components/error.vue' import { onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
let timer: any = null
const second = ref(5)
const router = useRouter()
timer = setInterval(() => {
if (second.value === 0) {
clearInterval(timer)
router.go(-1)
} else {
second.value--
}
}, 1000)
onUnmounted(() => {
timer && clearInterval(timer)
})
</script> </script>
<style lang="scss" scoped>
.error {
text-align: center;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
.error-code {
@apply text-primary;
font-size: 150px;
}
.el-button {
width: 176px;
}
}
</style>

View File

@ -1,58 +0,0 @@
<template>
<div class="error">
<div>
<slot name="content">
<div class="error-code">{{ code }}</div>
</slot>
<div class="text-lg text-tx-secondary mt-7 mb-7">{{ title }}</div>
<el-button v-if="showBtn" type="primary" @click="router.go(-1)">
{{ second }} 秒后返回上一页
</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
const props = defineProps({
code: String,
title: String,
showBtn: {
type: Boolean,
default: true
}
})
let timer: any = null
const second = ref(5)
const router = useRouter()
props.showBtn &&
(timer = setInterval(() => {
if (second.value === 0) {
clearInterval(timer)
router.go(-1)
} else {
second.value--
}
}, 1000))
onUnmounted(() => {
timer && clearInterval(timer)
})
</script>
<style lang="scss" scoped>
.error {
text-align: center;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
.error-code {
@apply text-primary;
font-size: 150px;
}
.el-button {
width: 176px;
}
}
</style>

View File

@ -0,0 +1,332 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-card class="box-card !border-none my-[16px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="orderTableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('cashOutStatus')" prop="order_from">
<el-select v-model="orderTableData.searchParam.status" clearable class="input-width">
<el-option :label="t('selectPlaceholder')" value="" />
<el-option :label="item" :value="key" v-for="(item, key) in cashOutStatusList" />
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="orderTableData.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="loadOrderList()">{{ t('search') }}</el-button>
<el-button @click="searchFormRef?.resetFields()">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[16px]">
<el-table :data="orderTableData.data" size="large" v-loading="orderTableData.loading">
<template #empty>
<span>{{ !orderTableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="order_no" :show-overflow-tooltip="true" :label="t('memberInfo')" align="center" min-width="140">
<template #default="{ row }">
<div class="flex items-center cursor-pointer " @click="toMember(row.member_id)">
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.headimg" :src="img(row.headimg)" alt="" >
<img class="w-[50px] h-[50px] mr-[10px]" v-else src="@/assets/images/default_headimg.png" alt="" >
<div class="flex flex flex-col">
<span class="text-blue-700">{{ row.nickname || '' }}</span>
<span class="text-blue-700">{{ row.mobile || '' }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="account_type_name" :label="t('cashOutAccountType')" align="center" min-width="140" />
<el-table-column :label="t('cashOutMethod')" align="center" min-width="140" >
<template #default="{ row }">
{{ Transfertype[row.transfer_type].name }}
</template>
</el-table-column>
<el-table-column prop="apply_money" :label="t('applicationForWithdrawalAmount')" min-width="140" align="center" />
<!--
<el-table-column prop="service_money" :label="t('cashOutCommission')" align="center" min-width="140" />
-->
<el-table-column prop="money" :label="t('cashOutMoney')" min-width="200" align="center">
<template #default="{ row }">
<div>{{ t('actualTransferAmount') }} {{ row.money }}</div>
<div>{{ t('cashOutCommission') }} {{ row.service_money }}</div>
</template>
</el-table-column>
<el-table-column prop="status_name" :label="t('cashOutStatus')" align="center" min-width="100" />
<el-table-column :label="t('applyTime')" min-width="180" align="center">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" width="230">
<template #default="{ row }">
<el-button v-for="(item,index) in operationBtn[row.status.toString()].value" :key="index+'a'" @click="fnProcessing(operationBtn[row.status.toString()].clickArr[index],row)" type="primary" link>{{ item }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="orderTableData.page" v-model:page-size="orderTableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="orderTableData.total"
@size-change="loadOrderList()" @current-change="loadOrderList" />
</div>
</div>
</el-card>
<!-- 详情 -->
<el-dialog v-model="cashOutShowDialog" :title="t('cashOutDetail')" width="500px" :destroy-on-close="true">
<el-form :model="cashOutInfo" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="cashOutLoading">
<el-form-item :label="t('nickname')">
<div class="input-width"> {{ cashOutInfo.nickname }} </div>
</el-form-item>
<el-form-item :label="t('cashOutAccountType')">
<div class="input-width"> {{ cashOutInfo.account_type_name }} </div>
</el-form-item>
<el-form-item :label="t('cashOutMethod')">
<div class="input-width"> {{ Transfertype[cashOutInfo.transfer_type].name }} </div>
</el-form-item>
<el-form-item :label="t('applicationForWithdrawalAmount')">
<div class="input-width"> {{ cashOutInfo.apply_money }} </div>
</el-form-item>
<el-form-item :label="t('cashOutCommission')">
<div class="input-width"> {{ cashOutInfo.service_money }} </div>
</el-form-item>
<el-form-item :label="t('actualTransferAmount')">
<div class="input-width"> {{ cashOutInfo.money }} </div>
</el-form-item>
<el-form-item :label="t('cashOutStatus')">
<div class="input-width"> {{ cashOutInfo.status_name }} </div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="cashOutShowDialog = false">{{t('confirm')}}</el-button>
</span>
</template>
</el-dialog>
<!-- 是否审核 -->
<el-dialog v-model="auditShowDialog" :title="t('rejectionAudit')" width="500px" :destroy-on-close="true">
<el-form :model="auditFailure" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('reasonsRefusal')" prop="label_name">
<el-input v-model="auditFailure.refuse_reason" clearable :placeholder="t('reasonsRefusalPlaceholder')" class="input-width" type="textarea" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="auditShowDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
t('confirm')
}}</el-button>
</span>
</template>
</el-dialog>
<!-- 是否转账 -->
<el-dialog v-model="transferShowDialog" :title="t('rejectionAudit')" width="500px" :destroy-on-close="true">
<p>{{t('isTransfer')}}</p>
<template #footer>
<span class="dialog-footer">
<el-button @click="transferShowDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirm(formRef)">{{
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 { getWithdrawList, getTransfertype, memberTransfer, memberAudit, getWithdrawDetail, getWithdrawStatusList } from '@/api/member'
import { img } from '@/utils/common'
import { ElMessageBox } from 'element-plus'
import { data } from 'dom7'
import { useRouter, useRoute } from 'vue-router'
const cashOutStatusList = ref([])
const checkStatusList = async () => {
cashOutStatusList.value = await (await getWithdrawStatusList({})).data
}
checkStatusList()
const route = useRoute()
const router = useRouter()
const member_id: number = parseInt(route.query.id || 0)
const operationBtn = ref({
"1": {
value: [t('successfulAudit'),t('auditFailure'),t('detail')],
clickArr: ['successfulAuditFn','auditFailureFn','detailFn']
},
"2": {
value: [t('transfer'),t('detail')],
clickArr: ['transferFn','detailFn']
},
"3": {
value: [t('detail')],
clickArr: ['detailFn']
},
"-1": {
value: [t('detail')],
clickArr: ['detailFn']
},
"-2": {
value: [t('detail')],
clickArr: ['detailFn']
}
});
const orderTableData = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
order_no: '',
member_id,
create_time: [],
status: ''
}
})
//
const Transfertype = ref<Array<Object>>([])
const getTransfertypeFn = async()=>{
Transfertype.value = await (await getTransfertype()).data
}
getTransfertypeFn()
/**
* 获取提现列表
*/
const loadOrderList = (page: number = 1) => {
orderTableData.loading = true
orderTableData.page = page
getWithdrawList({
page: orderTableData.page,
limit: orderTableData.limit,
...orderTableData.searchParam
}).then(res => {
orderTableData.loading = false
orderTableData.data = res.data.data
orderTableData.total = res.data.total
}).catch(() => {
orderTableData.loading = false
})
}
loadOrderList()
//
let auditFailure = ref({refuse_reason:'',id:0,action: 0})
let auditShowDialog = ref(false);
const fnProcessing = (type:string, data: any)=>{
let obj = {}
if(['successfulAuditFn','auditFailureFn'].includes(type)){
obj.id = data.id;
if(type == 'successfulAuditFn'){
obj.action = 'agree';
cashOutAuditFn(obj)
}else{
obj.action = 'refuse';
auditFailure.value = Object.assign(auditFailure.value,obj);
auditShowDialog.value = true;
}
}else if(type == 'transferFn'){
obj.id = data.id;
ElMessageBox.confirm(`${t('isTransfer')}`,`${t('transfer')}`)
.then(() => {
transferFn(obj);
})
}else{
detailFn(data.id);
}
}
/**
* 转账
* @param data
*/
const transferFn = (data)=>{
memberTransfer({...data}).then(res => {
loadOrderList()
}).catch(() => {
loadOrderList()
})
}
/**
* 详情
* @param data
*/
let cashOutShowDialog = ref(false);
let cashOutInfo = ref({});
let cashOutLoading = ref(true);
const detailFn = (id)=>{
getWithdrawDetail(id).then(res => {
cashOutInfo.value = res.data;
cashOutShowDialog.value = true;
cashOutLoading.value = false;
}).catch(() => {
loadOrderList()
})
}
/**
* 提现审核
* @param data
*/
const cashOutAuditFn = (data)=>{
memberAudit({
...data
}).then(res => {
loadOrderList()
}).catch(() => {
loadOrderList()
})
}
/**
* 拒绝审核
* @param data
*/
const confirm = ()=>{
auditShowDialog.value = false;
cashOutAuditFn(auditFailure.value);
}
/**
* 订单详情
* @param data
*/
const infoEvent = (data: any) => {
router.push(`/finance/recharge/detail?order_id=${data.order_id}`)
}
/**
* 会员详情
*/
const toMember = (member_id: number) => {
router.push(`/member/detail?id=${member_id}`)
}
</script>
<style lang="scss" scoped></style>

View File

@ -2,7 +2,7 @@
<div class="main-container"> <div class="main-container">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<el-card class="box-card !border-none my-[16px] table-search-wrap" shadow="never"> <el-card class="box-card !border-none mb-[16px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="orderTableData.searchParam" ref="searchFormRef"> <el-form :inline="true" :model="orderTableData.searchParam" ref="searchFormRef">
<!-- <el-form-item :label="t('orderNo')" prop="order_no"> <!-- <el-form-item :label="t('orderNo')" prop="order_no">
<el-input v-model="orderTableData.searchParam.order_no" :placeholder="t('orderNoPlaceholder')" /> <el-input v-model="orderTableData.searchParam.order_no" :placeholder="t('orderNoPlaceholder')" />
@ -73,9 +73,10 @@
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="infoEvent(row)">{{ t('info') }}</el-button> <el-button type="primary" link @click="infoEvent(row)">{{ t('info') }}</el-button>
<el-button v-if="[1, 10].includes(row.order_status_info.status)" type="primary" link @click="refundBtnFn(row)">{{ t('refundBtn') }}</el-button>
<template v-for="(item, index) in row.order_status_info.action"> <template v-for="(item, index) in row.order_status_info.action">
<el-button type="danger" link @click="orderEvent(row, item.class)">{{ item.name <el-button type="danger" link @click="orderEvent(row, item.class)">{{ item.name }}</el-button>
}}</el-button>
</template> </template>
</template> </template>
</el-table-column> </el-table-column>
@ -87,20 +88,28 @@
@size-change="loadOrderList()" @current-change="loadOrderList" /> @size-change="loadOrderList()" @current-change="loadOrderList" />
</div> </div>
</div> </div>
</el-card> </el-card>
<!-- 是否退款 -->
<el-dialog v-model="refundShowDialog" :title="t('refundBtn')" width="500px" :destroy-on-close="true">
<p>{{t('refundContent')}}</p>
<template #footer>
<span class="dialog-footer">
<el-button @click="refundShowDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { getRechargeOrderStatusList, getRechargeOrderList } from '@/api/order' import { getRechargeOrderStatusList, getRechargeOrderList, rechargeRefund } from '@/api/order'
import { getChannelType } from '@/api/sys' import { getChannelType } from '@/api/sys'
import { img } from '@/utils/common'
import { ElMessageBox } from 'element-plus'
import { data } from 'dom7'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const member_id: number = parseInt(route.query.id || 0) const member_id: number = parseInt(route.query.id || 0)
@ -169,6 +178,32 @@ const infoEvent = (data: any) => {
const orderEvent = (data: any, type: string) => { const orderEvent = (data: any, type: string) => {
} }
/**
* 退款操作
*/
let refundShowDialog = ref(false);
const refundFn = (data) => {
console.log("退款操作",data);
refundShowDialog.value = true;
rechargeRefund(data.order_id).then(res => {
refundShowDialog.value = false;
}).catch(() => {
})
// getRechargeOrderList({
// page: orderTableData.page,
// limit: orderTableData.limit,
// ...orderTableData.searchParam
// }).then(res => {
// orderTableData.loading = false
// orderTableData.data = res.data.data
// orderTableData.total = res.data.total
// }).catch(() => {
// orderTableData.loading = false
// })
}
/** /**
* 会员详情 * 会员详情

Some files were not shown because too many files have changed in this diff Show More