This commit is contained in:
全栈小学生 2025-05-23 14:31:53 +08:00
parent c337da9ea3
commit d98c2dacc1
137 changed files with 5696 additions and 2947 deletions

View File

@ -14,7 +14,7 @@ export function getAliappConfig() {
* @returns
*/
export function setAliappConfig(params: Record<string, any>) {
return request.put('aliapp/config', params, {showSuccessMessage: true})
return request.put('aliapp/config', params, { showSuccessMessage: true })
}
/**

View File

@ -1,12 +1,11 @@
import request from '@/utils/request'
/**
*
* @param params
*/
export function login(params: Record<string, any>) {
return request.get(`login`, {params})
return request.get(`login`, { params })
}
/**
@ -21,15 +20,7 @@ export function logout() {
* @returns
*/
export function getAuthMenus(params: Record<string, any>) {
return request.get('auth/authmenu', {params})
}
/**
*
* @returns
*/
export function getSiteInfo() {
return request.get('auth/site')
return request.get('auth/authmenu', { params })
}
/**

View File

@ -8,7 +8,7 @@ import request from '@/utils/request'
* @returns
*/
export function getDiyPageList(params: Record<string, any>) {
return request.get(`diy/diy`, {params})
return request.get(`diy/diy`, { params })
}
/**
@ -17,7 +17,7 @@ export function getDiyPageList(params: Record<string, any>) {
* @returns
*/
export function getDiyPageListByCarouselSearch(params: Record<string, any>) {
return request.get(`diy/carousel_search`, {params})
return request.get(`diy/carousel_search`, { params })
}
/**
@ -26,7 +26,7 @@ export function getDiyPageListByCarouselSearch(params: Record<string, any>) {
* @returns
*/
export function getDiyList(params: Record<string, any>) {
return request.get(`diy/list`, {params})
return request.get(`diy/list`, { params })
}
/**
@ -35,7 +35,7 @@ export function getDiyList(params: Record<string, any>) {
* @returns
*/
export function getDiyPageInfo(id: number) {
return request.get(`diy/diy/${id}`);
return request.get(`diy/diy/${ id }`);
}
/**
@ -44,7 +44,7 @@ export function getDiyPageInfo(id: number) {
* @returns
*/
export function addDiyPage(params: Record<string, any>) {
return request.post('diy/diy', params, {showSuccessMessage: true})
return request.post('diy/diy', params, { showSuccessMessage: true })
}
/**
@ -52,7 +52,7 @@ export function addDiyPage(params: Record<string, any>) {
* @param params
*/
export function editDiyPage(params: Record<string, any>) {
return request.put(`diy/diy/${params.id}`, params, {showSuccessMessage: true})
return request.put(`diy/diy/${ params.id }`, params, { showSuccessMessage: true })
}
/**
@ -60,7 +60,7 @@ export function editDiyPage(params: Record<string, any>) {
* @param params
*/
export function setUseDiyPage(params: Record<string, any>) {
return request.put(`diy/use/${params.id}`, params, {showSuccessMessage: true})
return request.put(`diy/use/${ params.id }`, params, { showSuccessMessage: true })
}
/**
@ -68,7 +68,7 @@ export function setUseDiyPage(params: Record<string, any>) {
* @param params
*/
export function editDiyPageShare(params: Record<string, any>) {
return request.put(`diy/diy/share`, params, {showSuccessMessage: true})
return request.put(`diy/diy/share`, params, { showSuccessMessage: true })
}
/**
@ -77,35 +77,35 @@ export function editDiyPageShare(params: Record<string, any>) {
* @returns
*/
export function deleteDiyPage(id: number) {
return request.delete(`diy/diy/${id}`, {showSuccessMessage: true})
return request.delete(`diy/diy/${ id }`, { showSuccessMessage: true })
}
/**
*
*/
export function initPage(params: Record<string, any>) {
return request.get(`diy/init`, {params})
return request.get(`diy/init`, { params })
}
/**
*
*/
export function getLink(params: Record<string, any>) {
return request.get(`diy/link`, {params})
return request.get(`diy/link`, { params })
}
/**
*
*/
export function getDiyBottomList(params: Record<string, any>) {
return request.get(`diy/bottom`, {params})
return request.get(`diy/bottom`, { params })
}
/**
*
*/
export function getDiyBottomConfig(params: Record<string, any>) {
return request.get(`diy/bottom/config`, {params})
return request.get(`diy/bottom/config`, { params })
}
/**
@ -114,21 +114,21 @@ export function getDiyBottomConfig(params: Record<string, any>) {
* @returns
*/
export function setDiyBottomConfig(params: Record<string, any>) {
return request.post('diy/bottom', params, {showSuccessMessage: true})
return request.post('diy/bottom', params, { showSuccessMessage: true })
}
/**
*
*/
export function getDiyTemplate(params: Record<string, any>) {
return request.get(`diy/template`, {params})
return request.get(`diy/template`, { params })
}
/**
*
*/
export function getDiyTemplatePages(params: Record<string, any>) {
return request.get(`diy/template/pages`, {params})
return request.get(`diy/template/pages`, { params })
}
/**
@ -137,7 +137,7 @@ export function getDiyTemplatePages(params: Record<string, any>) {
* @returns
*/
export function getDiyRouteList(params: Record<string, any>) {
return request.get(`diy/route`, {params})
return request.get(`diy/route`, { params })
}
/**
@ -145,7 +145,7 @@ export function getDiyRouteList(params: Record<string, any>) {
* @returns
*/
export function getDiyRouteAppList() {
return request.get(`diy/route/apps`)
return request.get(`diy/route/apps`)
}
/**
@ -153,7 +153,7 @@ export function getDiyRouteAppList() {
* @param params
*/
export function getDiyRouteInfo(params: Record<string, any>) {
return request.get(`diy/route/info`, {params});
return request.get(`diy/route/info`, { params });
}
/**
@ -161,7 +161,7 @@ export function getDiyRouteInfo(params: Record<string, any>) {
* @param params
*/
export function editDiyRouteShare(params: Record<string, any>) {
return request.put(`diy/route/share`, params, {showSuccessMessage: true})
return request.put(`diy/route/share`, params, { showSuccessMessage: true })
}
/**
@ -170,7 +170,7 @@ export function editDiyRouteShare(params: Record<string, any>) {
* @returns
*/
export function getDecoratePage(params: Record<string, any>) {
return request.get(`diy/decorate`, {params})
return request.get(`diy/decorate`, { params })
}
/**
@ -179,7 +179,7 @@ export function getDecoratePage(params: Record<string, any>) {
* @returns
*/
export function changeTemplate(params: Record<string, any>) {
return request.put(`diy/change`, params, {showSuccessMessage: true})
return request.put(`diy/change`, params, { showSuccessMessage: true })
}
/**
@ -188,7 +188,7 @@ export function changeTemplate(params: Record<string, any>) {
* @returns
*/
export function getApps(params: Record<string, any>) {
return request.get(`diy/apps`)
return request.get(`diy/apps`)
}
/**
@ -207,7 +207,7 @@ export function copyDiy(params: Record<string, any>) {
* @param params
*/
export function getDefaultTheme(params: Record<string, any>) {
return request.get(`diy/theme/color`, {params})
return request.get(`diy/theme/color`, { params })
}
@ -216,7 +216,7 @@ export function getDefaultTheme(params: Record<string, any>) {
* @param params
*/
export function getDiyTheme(params: Record<string, any>) {
return request.get(`diy/theme`, {params})
return request.get(`diy/theme`, { params })
}
/**
@ -232,7 +232,7 @@ export function addTheme(params: Record<string, any>) {
* @param params
*/
export function editTheme(params: Record<string, any>) {
return request.put(`diy/theme/edit/${params.id}`, params, {showSuccessMessage: true})
return request.put(`diy/theme/edit/${ params.id }`, params, { showSuccessMessage: true })
}
/**
@ -240,13 +240,13 @@ export function editTheme(params: Record<string, any>) {
* @param params
*/
export function deleteTheme(id: number) {
return request.delete(`diy/theme/delete/${id}`, {showSuccessMessage: true})
return request.delete(`diy/theme/delete/${ id }`, { showSuccessMessage: true })
}
/**
*
*
* @param params
*/
export function setDiyTheme(params: Record<string, any>) {
return request.post(`diy/theme`, params, { showSuccessMessage: true })
}
}

View File

@ -19,6 +19,7 @@ export function getDiyFormPageList(params: Record<string, any>) {
export function getDiyFormList(params: Record<string, any>) {
return request.get(`diy/form/list`, { params })
}
/**
*
* @param params

View File

@ -11,7 +11,7 @@ export function getAuthInfo() {
*
*/
export function setAuthInfo(params: Record<string, any>) {
return request.post('niucloud/authinfo', params, {showSuccessMessage: true})
return request.post('niucloud/authinfo', params, { showSuccessMessage: true })
}
/**
@ -43,7 +43,7 @@ export function getModuleVersion() {
* @returns
*/
export function downloadVersion(params: Record<string, any>) {
return request.post(`addon/download/${params.addon}`, params, { showSuccessMessage: true })
return request.post(`addon/download/${ params.addon }`, params, { showSuccessMessage: true })
}
/**
@ -61,3 +61,11 @@ export function getFrameworkNewVersion() {
export function getFrameworkVersionList() {
return request.get(`niucloud/framework/version/list`)
}
/**
* /
* @param params
*/
export function getAppVersionList(params: Record<string, any>) {
return request.get(`niucloud/app_version/list`, { params })
}

View File

@ -7,7 +7,7 @@ import request from '@/utils/request'
* @returns
*/
export function getNoticeList(params: any) {
return request.get('notice/notice', {params})
return request.get('notice/notice', { params })
}
/**
@ -16,7 +16,7 @@ export function getNoticeList(params: any) {
* @returns
*/
export function getNoticeLog(params: any) {
return request.get(`notice/log`, {params})
return request.get(`notice/log`, { params })
}
/**
@ -25,7 +25,7 @@ export function getNoticeLog(params: any) {
* @returns
*/
export function getNoticeInfo(key: string) {
return request.get(`notice/notice/${key}`)
return request.get(`notice/notice/${ key }`)
}
/**
@ -34,7 +34,7 @@ export function getNoticeInfo(key: string) {
* @returns
*/
export function editNoticeStatus(params: Record<string, any>) {
return request.post(`notice/notice/editstatus`, params, {showSuccessMessage: true})
return request.post(`notice/notice/editstatus`, params, { showSuccessMessage: true })
}
/**
@ -43,7 +43,7 @@ export function editNoticeStatus(params: Record<string, any>) {
* @returns
*/
export function editNotice(params: Record<string, any>) {
return request.post(`notice/notice/edit`, params, {showSuccessMessage: true})
return request.post(`notice/notice/edit`, params, { showSuccessMessage: true })
}
/**
@ -60,7 +60,7 @@ export function getSmsList() {
* @returns
*/
export function getSmsInfo(sms_type: string) {
return request.get(`notice/notice/sms/${sms_type}`,)
return request.get(`notice/notice/sms/${ sms_type }`,)
}
/**
@ -68,7 +68,7 @@ export function getSmsInfo(sms_type: string) {
* @param params
*/
export function editSms(params: Record<string, any>) {
return request.put(`notice/notice/sms/${params.sms_type}`, params, {showSuccessMessage: true})
return request.put(`notice/notice/sms/${ params.sms_type }`, params, { showSuccessMessage: true })
}
/**

View File

@ -54,6 +54,14 @@ export function getPayRefundInfo(refund_no: string) {
return request.get(`pay/refund/${refund_no}`)
}
/**
* 退
* @param refund_no
*/
export function getRefundStatus() {
return request.get(`pay/refund/status`)
}
/**
* 退
*/

View File

@ -14,5 +14,5 @@ export function getPcConfig() {
* @returns
*/
export function setPcConfig(params: Record<string, any>) {
return request.put('channel/pc/config', params, {showSuccessMessage: true})
return request.put('channel/pc/config', params, { showSuccessMessage: true })
}

View File

@ -13,6 +13,6 @@ export function getUserInfo() {
* @returns
*/
export function setUserInfo(params: Record<string, any>) {
return request.put(`auth/edit`, params, {showSuccessMessage: true});
return request.put(`auth/edit`, params, { showSuccessMessage: true });
}

View File

@ -6,7 +6,7 @@ import request from '@/utils/request'
* @returns
*/
export function getPosterPageList(params: Record<string, any>) {
return request.get(`sys/poster`, {params})
return request.get(`sys/poster`, { params })
}
/**
@ -15,7 +15,7 @@ export function getPosterPageList(params: Record<string, any>) {
* @returns
*/
export function getPosterList(params: Record<string, any>) {
return request.get(`sys/poster/list`, {params})
return request.get(`sys/poster/list`, { params })
}
/**
@ -24,7 +24,7 @@ export function getPosterList(params: Record<string, any>) {
* @returns
*/
export function getPosterInfo(id: number) {
return request.get(`sys/poster/${id}`);
return request.get(`sys/poster/${ id }`);
}
/**
@ -33,7 +33,7 @@ export function getPosterInfo(id: number) {
* @returns
*/
export function addPoster(params: Record<string, any>) {
return request.post('sys/poster', params, {showErrorMessage: true, showSuccessMessage: true})
return request.post('sys/poster', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -42,7 +42,7 @@ export function addPoster(params: Record<string, any>) {
* @returns
*/
export function editPoster(params: Record<string, any>) {
return request.put(`sys/poster/${params.id}`, params, {
return request.put(`sys/poster/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
@ -54,7 +54,7 @@ export function editPoster(params: Record<string, any>) {
* @returns
*/
export function deletePoster(id: number) {
return request.delete(`sys/poster/${id}`, {showErrorMessage: true, showSuccessMessage: true})
return request.delete(`sys/poster/${ id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -62,7 +62,7 @@ export function deletePoster(id: number) {
* @param params
*/
export function modifyPosterStatus(params: Record<string, any>) {
return request.put(`sys/poster/status`, params, {showSuccessMessage: true})
return request.put(`sys/poster/status`, params, { showSuccessMessage: true })
}
/**
@ -70,7 +70,7 @@ export function modifyPosterStatus(params: Record<string, any>) {
* @param params
*/
export function modifyPosterDefault(params: Record<string, any>) {
return request.put(`sys/poster/default`, params, {showSuccessMessage: true})
return request.put(`sys/poster/default`, params, { showSuccessMessage: true })
}
/**
@ -79,7 +79,7 @@ export function modifyPosterDefault(params: Record<string, any>) {
* @returns
*/
export function getPosterType(params: Record<string, any>) {
return request.get(`sys/poster/type`, {params})
return request.get(`sys/poster/type`, { params })
}
/**
@ -88,21 +88,21 @@ export function getPosterType(params: Record<string, any>) {
* @returns
*/
export function getPosterTemplate(params: Record<string, any>) {
return request.get(`sys/poster/template`, {params})
return request.get(`sys/poster/template`, { params })
}
/**
*
*/
export function initPoster(params: Record<string, any>) {
return request.get(`sys/poster/init`, {params})
return request.get(`sys/poster/init`, { params })
}
/**
*
*/
export function getPreviewPoster(params: Record<string, any>) {
return request.get(`sys/poster/preview`, {params})
return request.get(`sys/poster/preview`, { params })
}
/**
@ -111,5 +111,5 @@ export function getPreviewPoster(params: Record<string, any>) {
* @returns
*/
export function getPosterGenerate(params: Record<string, any>) {
return request.get(`sys/poster/generate`, {params, showErrorMessage: false})
}
return request.get(`sys/poster/generate`, { params, showErrorMessage: false })
}

View File

@ -727,3 +727,11 @@ export function deleteExport(id: number) {
export function getInstallConfig() {
return request.get('sys/install/config')
}
/**
*
* @returns
*/
export function getQrcode(params: Record<string, any>) {
return request.get(`sys/qrcode`, { params, showErrorMessage: false })
}

View File

@ -6,7 +6,7 @@ import request from '@/utils/request'
* @returns
*/
export function getAddonDevelop(params: Record<string, any>) {
return request.get(`addon_develop`, {params});
return request.get(`addon_develop`, { params });
}
/**
@ -22,7 +22,7 @@ export function getAddontype() {
* @returns
*/
export function getAddonDevelopInfo(key: any) {
return request.get(`addon_develop/${key}`)
return request.get(`addon_develop/${ key }`)
}
/**
@ -30,7 +30,7 @@ export function getAddonDevelopInfo(key: any) {
* @returns
*/
export function getAddonDevelopCheck(key: any) {
return request.get(`addon_develop/check/${key}`)
return request.get(`addon_develop/check/${ key }`)
}
/**
@ -48,7 +48,7 @@ export function getAddonKeyBlackList() {
* @returns
*/
export function addAddonDevelop(key: any, params: Record<string, any>) {
return request.post(`addon_develop/${key}`, params)
return request.post(`addon_develop/${ key }`, params)
}
/**
@ -58,7 +58,7 @@ export function addAddonDevelop(key: any, params: Record<string, any>) {
* @returns
*/
export function editAddonDevelop(key: any, params: Record<string, any>) {
return request.put(`addon_develop/${key}`, params)
return request.put(`addon_develop/${ key }`, params)
}
/**
@ -67,7 +67,7 @@ export function editAddonDevelop(key: any, params: Record<string, any>) {
* @returns
*/
export function deleteAddonDevelop(key: any) {
return request.delete(`addon_develop/${key}`, {showSuccessMessage: true})
return request.delete(`addon_develop/${ key }`, { showSuccessMessage: true })
}
/**
@ -75,7 +75,7 @@ export function deleteAddonDevelop(key: any) {
* @returns
*/
export function addonDevelopBuild(key: any) {
return request.post(`addon_develop/build/${key}`)
return request.post(`addon_develop/build/${ key }`)
}
/**
@ -83,8 +83,9 @@ export function addonDevelopBuild(key: any) {
* @returns
*/
export function addonDevelopDownload(key: any) {
return request.post(`addon_develop/download/${key}`, {})
return request.post(`addon_develop/download/${ key }`, {})
}
/***************************************************** 代码生成 ****************************************************/
/**
@ -93,7 +94,7 @@ export function addonDevelopDownload(key: any) {
* @returns
*/
export function getGenerateTableList(params: Record<string, any>) {
return request.get(`generator/generator`, {params})
return request.get(`generator/generator`, { params })
}
/**
@ -102,7 +103,7 @@ export function getGenerateTableList(params: Record<string, any>) {
* @returns
*/
export function getGenerateTableInfo(id: number) {
return request.get(`generator/generator/${id}`);
return request.get(`generator/generator/${ id }`);
}
/**
@ -111,7 +112,7 @@ export function getGenerateTableInfo(id: number) {
* @returns
*/
export function addGenerateTable(params: Record<string, any>) {
return request.post('generator/generator', params, {showSuccessMessage: true})
return request.post('generator/generator', params, { showSuccessMessage: true })
}
/**
@ -119,7 +120,7 @@ export function addGenerateTable(params: Record<string, any>) {
* @param params
*/
export function editGenerateTable(params: Record<string, any>) {
return request.put(`generator/generator/${params.id}`, params, {showSuccessMessage: true})
return request.put(`generator/generator/${ params.id }`, params, { showSuccessMessage: true })
}
/**
@ -128,7 +129,7 @@ export function editGenerateTable(params: Record<string, any>) {
* @returns
*/
export function deleteGenerateTable(id: number) {
return request.delete(`generator/generator/${id}`, {showSuccessMessage: true})
return request.delete(`generator/generator/${ id }`, { showSuccessMessage: true })
}
/**
@ -146,7 +147,7 @@ export function generateCreate(params: Record<string, any>) {
* @returns
*/
export function generatePreview(id: number) {
return request.get(`generator/preview/${id}`)
return request.get(`generator/preview/${ id }`)
}
/**
@ -167,26 +168,26 @@ export function getSystem() {
*
*/
export function getGeneratorAllModel(params: any) {
return request.get(`generator/all_model`, {params})
return request.get(`generator/all_model`, { params })
}
/**
*
*/
export function getGeneratorTableColumn(params: any) {
return request.get(`generator/table_column`, {params})
return request.get(`generator/table_column`, { params })
}
/**
*
*/
export function generatorCheckFile(params: Record<string, any>) {
return request.get(`generator/check_file`, {params})
return request.get(`generator/check_file`, { params })
}
/**
*
*/
export function getGeneratorModelTableColumn(params: any) {
return request.get(`generator/model_table_column`, {params})
return request.get(`generator/model_table_column`, { params })
}

View File

@ -6,7 +6,7 @@ import request from '@/utils/request'
* @param addon
*/
export function getUpgradeContent(addon: string = '') {
return request.get(addon ? `upgrade/${addon}` : 'upgrade')
return request.get(addon ? `upgrade/${ addon }` : 'upgrade')
}
/**
@ -20,22 +20,22 @@ export function getUpgradeTask() {
*
* @param addon
*/
export function upgradeAddon(addon: string = '') {
return request.post(addon ? `upgrade/${addon}` : 'upgrade')
export function upgradeAddon(addon: string = '', params: Record<string, any> = {}) {
return request.post(addon ? `upgrade/${ addon }` : 'upgrade', params)
}
/**
*
*/
export function executeUpgrade() {
return request.post('upgrade/execute', {})
return request.post('upgrade/execute', {}, { showErrorMessage: false })
}
/**
*
*/
export function preUpgradeCheck(addon: string = '') {
return request.get(addon ? `upgrade/check/${addon}` : 'upgrade/check')
return request.get(addon ? `upgrade/check/${ addon }` : 'upgrade/check')
}
/**
@ -44,3 +44,96 @@ export function preUpgradeCheck(addon: string = '') {
export function clearUpgradeTask() {
return request.post('upgrade/clear')
}
/**
*
* @param operate
*/
export function upgradeUserOperate(operate: string) {
return request.post(`upgrade/operate/${ operate }`)
}
/**
*
* @param params
* @returns
*/
export function getUpgradeRecords(params: Record<string, any>) {
return request.get(`upgrade/records`, { params })
}
/**
*
* @param params
*/
export function delUpgradeRecords(params: Record<string, any>) {
return request.delete(`upgrade/records`, { params })
}
/**
*
* @param params
* @returns
*/
export function getBackupRecords(params: Record<string, any>) {
return request.get(`backup/records`, { params })
}
/**
*
* @param params
*/
export function modifyBackupRemark(params: Record<string, any>) {
return request.put(`backup/remark`, params, { showSuccessMessage: true })
}
/**
*
*/
export function checkDirExist(params: Record<string, any>) {
return request.post('backup/check_dir', params)
}
/**
*
*/
export function checkPermission(params: Record<string, any>) {
return request.post('backup/check_permission', params)
}
/**
*
*/
export function restoreUpgradeBackup(params: Record<string, any>) {
return request.post('backup/restore', params)
}
/**
*
*/
export function deleteRecords(params: Record<string, any>) {
return request.post('backup/delete', params, { showSuccessMessage: true })
}
/**
*
*/
export function manualBackup(params: Record<string, any>) {
return request.post("backup/manual", params)
}
/**
*
* @param params
*/
export function performRecoveryTasks(params: Record<string, any>) {
return request.get("backup/restore_task", params)
}
/**
*
* @param params
*/
export function performBackupTasks(params: Record<string, any>) {
return request.get("backup/task", params)
}

View File

@ -18,7 +18,7 @@ export function getVerifyRecord(params: Record<string, any>) {
* @returns
*/
export function getVerifyDetail(verifyCode: string) {
return request.get(`verify/verify/${verifyCode}`)
return request.get(`verify/verify/${ verifyCode }`)
}
/***************************************************** 核销员 ****************************************************/
@ -64,5 +64,21 @@ export function addVerifier(params: Record<string, any>) {
* @returns
*/
export function deleteVerifier(id: number) {
return request.delete(`verify/verifier/${id}`, { showSuccessMessage: true })
}
return request.delete(`verify/verifier/${ id }`, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getVerifyInfo(id: number) {
return request.get(`verify/verifier/${ id }`)
}
/**
*
* @returns
*/
export function editVerifier(params: Record<string, any>) {
return request.post(`verify/verifier/${ params.id }`, params,{ showSuccessMessage: true })
}

View File

@ -33,6 +33,7 @@ export function getTemplateList() {
export function getBatchAcquisition(params: Record<string, any>) {
return request.put('weapp/template/sync', params, { showSuccessMessage: true })
}
/**
*
* @param params
@ -61,11 +62,11 @@ export function getWeappVersionList(params: Record<string, any>) {
/**
*
* @param key
* @returns
* @param key
* @returns
*/
export function getWeappUploadLog(key: string) {
return request.get(`weapp/upload/${key}`)
return request.get(`weapp/upload/${ key }`)
}
/***************************************************** 管理端 ****************************************************/
@ -103,7 +104,7 @@ export function getVersionList(params: Record<string, any>) {
* @returns
*/
export function getVersionInfo(id: string) {
return request.get(`applet/version/${id}`)
return request.get(`applet/version/${ id }`)
}
/**
@ -112,7 +113,7 @@ export function getVersionInfo(id: string) {
* @returns
*/
export function editVersion(params: Record<string, any>) {
return request.put(`applet/version/${params.id}`, params, { showSuccessMessage: true })
return request.put(`applet/version/${ params.id }`, params, { showSuccessMessage: true })
}
/**
@ -121,7 +122,7 @@ export function editVersion(params: Record<string, any>) {
* @returns
*/
export function deleteVersion(id: string) {
return request.delete(`applet/version/${id}`)
return request.delete(`applet/version/${ id }`)
}
/**
@ -130,4 +131,4 @@ export function deleteVersion(id: string) {
*/
export function getIsTradeManaged() {
return request.get('weapp/delivery/getIsTradeManaged')
}
}

View File

@ -21,7 +21,7 @@ export function getWechatStatic() {
* @returns
*/
export function editWechatConfig(params: Record<string, any>) {
return request.put('wechat/config', params, {showSuccessMessage: true})
return request.put('wechat/config', params, { showSuccessMessage: true })
}
/**
@ -38,7 +38,7 @@ export function getWechatMenu() {
* @returns
*/
export function editWechatMenu(params: Record<string, any>) {
return request.put('wechat/menu', params, {showSuccessMessage: true})
return request.put('wechat/menu', params, { showSuccessMessage: true })
}
/**
@ -55,7 +55,7 @@ export function getTemplateList() {
* @returns
*/
export function getBatchAcquisition(params: Record<string, any>) {
return request.put('wechat/template/sync', params, {showSuccessMessage: true})
return request.put('wechat/template/sync', params, { showSuccessMessage: true })
}
/**
@ -69,7 +69,7 @@ export function getKeywordsReplyList(params: Record<string, any>) {
*
*/
export function editKeywordsReply(params: Record<string, any>) {
return request.put(`wechat/reply/keywords/${params.id}`, params, { showSuccessMessage: true })
return request.put(`wechat/reply/keywords/${ params.id }`, params, { showSuccessMessage: true })
}
/**
@ -84,14 +84,14 @@ export function addKeywordsReply(params: Record<string, any>) {
* @param id
*/
export function getKeywordsReplyInfo(id: number) {
return request.get(`wechat/reply/keywords/${id}`)
return request.get(`wechat/reply/keywords/${ id }`)
}
/**
*
*/
export function delKeywordsReply(id: number) {
return request.delete(`wechat/reply/keywords/${id}`, { showSuccessMessage: true })
return request.delete(`wechat/reply/keywords/${ id }`, { showSuccessMessage: true })
}
/**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -84,7 +84,6 @@ const active = ref('build')
const cloudBuildCheck = ref<null | AnyObject>(null)
const loading = ref(false)
const terminalRef = ref(null)
const emits = defineEmits(['complete'])
let cloudBuildLog = []
/**
@ -127,7 +126,7 @@ const getCloudBuildLogFn = () => {
data[0].forEach(item => {
if (!cloudBuildLog.includes(item.action)) {
terminalRef.value.pushMessage({ content: `正在执行:${item.action}` })
terminalRef.value.pushMessage({ content: `${item.action}` })
cloudBuildLog.push(item.action)
if (item.code == 0) {

View File

@ -0,0 +1,240 @@
<template>
<el-dialog v-model="dialogVisible" :title="t('gxx')" width="850" :destroy-on-close="true">
<el-card class="box-card !border-none" shadow="never" >
<div v-loading="loading">
<div class="text-page-title mb-[20px]">历史版本</div>
<div class="time-dialog h-[500px]" style="overflow: auto">
<el-scrollbar>
<el-timeline style="width: 100%" v-if="!loading">
<el-timeline-item v-for="(item, index) in frameworkVersionList" :key="index" placement="left" :color="color">
<div class="relative">
<span class="text-[#333333] text-[14px] absolute">{{ timeSplit(item.release_time)[0] }}</span>
<br />
<span class="text-[#999999] text-[14px] w-[78px] block mt-[10px] absolute text-right">{{ timeSplit(item.release_time)[1] }}</span>
</div>
<el-collapse v-model="activeName" accordion>
<el-collapse-item :name="index">
<template #title>
<span class="text-[#333] text-[14px]"> v{{ item.version_no }} </span>
</template>
<div class="px-[20px] py-[20px] bg-overlay timeline-log-wrap whitespace-pre-wrap rounded-[4px]" style="background: rgba(25, 103, 249, 0.03);" v-if="item['upgrade_log']">
<div v-html="item['upgrade_log']"></div>
</div>
</el-collapse-item>
</el-collapse>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</div>
</div>
</el-card>
</el-dialog>
</template>
<script lang="ts" setup>
import { computed, ref, defineProps, nextTick } from "vue"
import { t } from "@/lang"
import { getAppVersionList, getFrameworkVersionList } from "@/app/api/module"
const props = defineProps({
upgradeKey: {
type: String,
required: true
}
})
const frameworkVersionList = ref([])
const newVersion: any = computed(() => {
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
})
const getAppVersionListFn = () => {
getAppVersionList({ app_key: props.upgradeKey }).then(({ data }) => {
loading.value = false
data.forEach((item: any, index) => {
if (index == 0) {
item.important = 1
} else {
item.important = 0
}
})
frameworkVersionList.value = data
})
}
const getFrameworkVersionListFn = () => {
getFrameworkVersionList().then(({ data }) => {
loading.value = false
data.forEach((item: any, index) => {
if (index == 0) {
item.important = 1
} else {
item.important = 0
}
})
frameworkVersionList.value = data
})
}
const activeName = ref(0)
//
const loading = ref(true)
const dialogVisible = ref(false)
const open = async () => {
nextTick(() => {
activeName.value = 0 //
if (props.upgradeKey) {
getAppVersionListFn()
} else {
getFrameworkVersionListFn()
}
dialogVisible.value = true
})
}
const timeSplit = (str: string) => {
const [date, time] = str.split(" ")
const [hours, minutes] = time.split(":")
return [date, `${ hours }:${ minutes }`]
}
defineExpose({
open
})
</script>
<style lang="scss" scoped></style>
<style scoped>
.el-timeline-item {
min-height: 75px;
}
.el-timeline-item >>> .el-timeline-item__node--normal {
left: 117px;
width: 18px;
height: 18px;
background: rgba(25, 103, 249, 0.12) !important;
border-radius: 50%;
/* 创建圆形 */
position: relative;
/* 用于定位伪元素 */
}
.el-timeline-item >>> .el-timeline-item__node--normal::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 8px;
/* 中心圆直径 */
height: 8px;
background-color: var(--el-color-primary);
/* 中心圆颜色 */
border-radius: 50%;
/* 中心圆为圆形 */
transform: translate(-50%, -50%);
/* 居中显示 */
}
.el-timeline-item >>> .el-timeline-item__tail {
left: 125px;
border-left-color: #dddddd;
border-left-style: dashed;
margin: 12px 0;
margin-top: 24px;
height: calc(100% - 24px - 12px);
}
.time-dialog >>> .el-dialog__header {
padding: 10px 20px;
height: 25px;
line-height: 25px;
text-align: left;
background: #fff;
border-bottom: solid 1px #e4e7ed;
}
.time-dialog >>> .el-card__body {
padding: 8px;
}
.time-dialog >>> .el-card.is-always-shadow {
box-shadow: none;
}
.time-dialog >>> .el-dialog__headerbtn .el-dialog__close {
color: #666;
}
.time-dialog >>> .el-dialog__headerbtn:hover .el-dialog__close {
color: #666;
}
.time-dialog >>> .el-dialog__headerbtn {
top: 14px;
}
.time-dialog >>> .el-collapse {
margin-left: 119px;
border: none;
margin-top: -22px;
}
.time-dialog >>> .el-collapse-item__header {
border: none;
line-height: 25px;
height: 25px;
position: relative;
z-index: 999;
}
.time-dialog >>> .el-collapse-item__wrap {
border: none;
}
.time-dialog >>> .el-collapse-item__content {
margin-top: 15px;
padding-bottom: 0px !important;
}
.time-dialog >>> .el-timeline-item__node--01 {
width: 18px !important;
height: 18px !important;
left: 117px !important;
}
</style>
<style>
.time-dialog .el-dialog {
margin: 0 auto !important;
max-height: 90%;
overflow: hidden;
top: 5%;
}
.time-dialog .el-dialog {
margin: 0 auto !important;
height: 65%;
overflow: hidden;
top: 10%;
}
.time-dialog .el-dialog__body {
position: absolute;
left: 0;
top: 46px;
bottom: 0;
right: 0;
z-index: 1;
overflow: hidden;
overflow-y: auto;
padding: 10px 20px 0 0;
}
.time-dialog .el-timeline-item__wrapper {
top: -20px !important;
}
.el-scrollbar__bar{
z-index:999;
}
</style>

View File

@ -1,111 +1,214 @@
<template>
<el-dialog v-model="showDialog" :title="t('upgrade.title')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :before-close="dialogClose">
<div v-show="active == 'content'">
<div class="h-[60vh] flex flex-col" v-if="upgradeContent">
<div class="text-lg">
本次升级将从<span class="font-bold">{{ upgradeContent.version }}</span>升级到<span class="font-bold">{{ upgradeContent.upgrade_version }}</span>版本
</div>
<div class="mt-[10px]" v-if="upgradeContent.upgrade_version != upgradeContent.last_version">
<el-alert type="info" show-icon>
<template #title>
当前最新版本为{{ upgradeContent.last_version }}您的服务{{ upgradeContent.expire_time ? `已于${upgradeContent.expire_time}到期` : '长期有效' }}如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级
<template v-if="upgradeContent">
<!-- 检测服务是否到期 -->
<template v-if="step == 1">
<template v-for="(item, index) in upgradeContent.content">
<div class="text-lg">
<template v-if="item.upgrade_version">
<span>{{ item.app.app_name }}本次升级将从</span>
<span class="font-bold px-[2px]">{{ item.version }}</span>
<span>升级到</span>
<span class="font-bold px-[2px]">{{ item.upgrade_version }}</span>
<span>版本</span>
</template>
<template v-else>
<template v-if="upgradeContent.content.length > 1">
<span>{{ item.app.app_name }}当前版本</span>
<span class="font-bold px-[2px]">{{ item.version }}</span>
</template>
<template v-else>
<span>当前版本</span>
<span class="font-bold px-[2px]">{{ item.version }}</span>
</template>
</template>
</el-alert>
</div>
<el-scrollbar class="flex-1 h-0 mt-[20px]">
<div class="mt-[20px]" v-for="(item, index) in upgradeContent.version_list" :key="index">
<div class="font-bold text-lg">{{ item.version_no }}</div>
<div class="mt-[5px]" v-if="item.release_time">{{ item.release_time }}</div>
<div class="mt-[10px] p-[10px] rounded bg-[#f4f4f5] whitespace-pre-wrap !break-all" v-if="item.upgrade_log" v-html="item.upgrade_log"></div>
</div>
</el-scrollbar>
</div>
<div class="flex justify-end" v-if="upgradeContent.version_list.length">
<el-button type="primary" @click="handleUpgrade" :loading="uploading">{{ t('upgrade.upgradeButton') }}</el-button>
</div>
</div>
<div class="mt-[10px]" :class="{ 'mb-[10px]' : (index + 1) < upgradeContent.content.length }" v-if="item.upgrade_version != item.last_version">
<el-alert type="info" show-icon :closable="false">
<template #title>
<span>当前最新版本为{{ item.last_version }}您的服务{{ item.expire_time ? `已于${item.expire_time}到期` : '长期有效' }}</span>
<span>如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级</span>
</template>
</el-alert>
</div>
</template>
</template>
<div v-if="step == 2">
<el-steps :active="numberOfSteps" align-center class="number-of-steps" finish-status="success" process-status="process">
<el-step :title="t('testDirectoryPermissions')" />
<el-step :title="t('backupFiles')" />
<el-step :title="t('startUpgrade')" />
<el-step :title="t('upgradeEnd')" />
</el-steps>
<div class="h-[400px]" style="overflow: auto">
<!-- <div class="time-dialog-wrap mt-[30px]" v-show="active == 'content'">-->
<!-- <el-timeline style="width: 100%">-->
<!-- <el-timeline-item v-for="(item, index) in upgradeContent.version_list" :key="index" placement="left">-->
<!-- <div class="relative">-->
<!-- <span class="text-[#333333] text-[16px] absolute">{{ timeSplit(item.release_time)[0] }}</span>-->
<!-- <br />-->
<!-- <span class="text-[#999999] text-[14px] w-[78px] block mt-[10px] absolute" style="text-align: right"> {{ timeSplit(item.release_time)[1] }}</span>-->
<!-- </div>-->
<!-- <el-collapse v-model="activeName" accordion>-->
<!-- <el-collapse-item :name="index">-->
<!-- <template #title>-->
<!-- <span class="text-[#333] text-[16px]"> v{{ item.version_no }} </span>-->
<!-- </template>-->
<div v-show="active == 'upgrade'">
<div class="h-[60vh] flex flex-col" v-if="upgradeCheck && !upgradeTask">
<el-scrollbar>
<div class="bg-[#fff] my-3" v-if="upgradeCheck.dir">
<p class="pt-[20px] pl-[20px] ">{{ t('upgrade.dirPermission') }}</p>
<div class="px-[20px] pt-[10px] text-[14px] el-table">
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
<el-col :span="12">
<span>{{ t('upgrade.path') }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.demand') }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('status') }}</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_readable">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.readable') }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_write">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.write') }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
<!-- <div class="px-[20px] py-[20px] bg-overlay timeline-log-wrap whitespace-pre-wrap rounded-[4px]" style="background: rgba(25, 103, 249, 0.03);" v-if="item['upgrade_log']">-->
<!-- <div v-html="item['upgrade_log']"></div>-->
<!-- </div>-->
<!-- </el-collapse-item>-->
<!-- </el-collapse>-->
<!-- </el-timeline-item>-->
<!-- </el-timeline>-->
<!-- </div>-->
<!-- 判断文件权限 -->
<div v-show="active == 'upgrade'">
<div class="flex flex-col" v-if="upgradeCheck && !upgradeTask">
<el-scrollbar>
<div class="bg-[#fff] my-3" v-if="upgradeCheck.dir">
<div class="px-[20px] pt-[10px] text-[14px] el-table">
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
<el-col :span="12">
<span>{{ t("upgrade.path") }}</span>
</el-col>
<el-col :span="6">
<span>{{ t("upgrade.demand") }}</span>
</el-col>
<el-col :span="6">
<span>{{ t("status") }}</span>
</el-col>
</el-row>
<div style="height: calc(300px); overflow: auto">
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_readable">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t("upgrade.readable") }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status">
<el-icon color="green">
<Select />
</el-icon>
</span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_write">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t("upgrade.write") }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status">
<el-icon color="green">
<Select />
</el-icon>
</span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
</div>
</div>
</div>
</el-scrollbar>
</div>
<div class="h-[370px] mt-[30px]" v-show="upgradeTask">
<terminal ref="terminalRef" :context="upgradeTask ? upgradeTask.upgrade.app_key : ''" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd" />
</div>
</div>
</el-scrollbar>
</div>
<div class="h-[60vh]" v-show="upgradeTask">
<terminal ref="terminalRef" :context="upgradeTask ? upgradeTask.upgrade.app_key : ''" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd"/>
</div>
</div>
<!-- 是否备份选择 -->
<div class="flex flex-col" v-show="active == 'backup'">
<el-scrollbar>
<div class="bg-[#fff] my-3">
<div v-show="active == 'complete'">
<div class="h-[60vh] flex flex-col">
<div class="flex-1 h-0">
<el-result icon="success" :title="t('upgrade.upgradeSuccess')"></el-result>
<el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" />
</div>
<div class="flex justify-end">
<el-button type="default" @click="showDialog = false">{{ t('upgrade.localBuild') }}</el-button>
<el-button type="primary" @click="handleCloudBuild">{{ t('upgrade.cloudBuild') }}</el-button>
<div class="px-[20px] pt-[10px] text-[14px] el-table" v-if="!upgradeContent.last_backup">
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
<el-col :span="20">
<span>功能操作</span>
</el-col>
<el-col :span="4">
<span>状态</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]" v-for="item in excludeSteps">
<el-col :span="20">
<span>{{ item.name }}</span>
</el-col>
<el-col :span="4">
<span>
<el-icon color="green">
<Select />
</el-icon>
</span>
</el-col>
</el-row>
</div>
<div class="pl-[50px] pt-[50px]" v-else>
<el-checkbox v-model="isNeedBackup" :label="t('upgrade.isNeedBackup')" :true-value="true" :false-value="false" size="large" >
</el-checkbox>
<div class="backup">{{ t('upgrade.isNeedBackupTips') }}<el-button link type="primary" @click="toBackupRecord">{{ t('upgrade.isNeedBackupBtn') }}</el-button></div>
</div>
</div>
</el-scrollbar>
</div>
<div class="mt-[50px]" v-show="active == 'complete'">
<el-result icon="success" :title="t('upgrade.upgradeSuccess')"></el-result>
<el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" v-show="upgradeTask && upgradeTask.executed && !upgradeTask.executed.includes('cloudBuild')"/>
</div>
</div>
</div>
</div>
</template>
<template #footer>
<div class="dialog-footer">
<!-- 查看升级内容 -->
<el-button v-if="step == 1 && upgradeContent.content.length && isAllowUpgrade" @click="step = 2" type="primary">{{ t("upgrade.upgradeButton") }}</el-button>
<template v-if="step == 2">
<!-- <el-button v-if="active == 'content'" @click="showDialog = false">{{ t("return") }}</el-button>-->
<el-button type="primary" :disabled="!is_pass" v-if="active == 'upgrade' && !upgradeTask" @click="() => { active = 'backup'; numberOfSteps = 1 }">{{ t("nextStep") }}</el-button>
<el-button v-if="active == 'backup'" @click="() => { active = 'upgrade'; numberOfSteps = 1 } ">{{ t("prev") }}</el-button>
<el-button type="primary" v-if="active == 'backup'" :loading="loading" @click="() => { upgradeAddonFn() }">{{ t("nextStep") }}</el-button>
<el-button v-if="active == 'complete'" @click="showDialog = false">{{ t("complete") }}</el-button>
</template>
</div>
</template>
</el-dialog>
<el-dialog v-model="upgradeTipsShowDialog" :title="t('warning')" width="500px" draggable>
<span v-html="t('upgrade.upgradeTips')"></span>
<template #footer>
<div class="flex justify-end">
<el-button @click="upgradeTipsConfirm(true)" type="primary">{{ t('upgrade.knownToKnow') }}</el-button>
<el-button @click="upgradeTipsConfirm()" type="primary" plain>{{ t('upgrade.upgradeButton') }}</el-button>
<el-button @click="upgradeTipsShowDialog = false">{{ t('cancel') }}</el-button>
<el-button @click="upgradeTipsConfirm(true)" type="primary">{{ t("upgrade.knownToKnow") }}</el-button>
<el-button @click="handleUpgrade()" type="primary" plain :loading="readyLoading">{{ t("upgrade.upgradeButton") }}</el-button>
<el-button @click="upgradeTipsShowDialog = false">{{ t("cancel") }}</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="cloudBuildErrorTipsShowDialog" :title="t('warning')" width="500px" draggable :show-close="false">
<span v-html="t('upgrade.cloudBuildErrorTips')"></span>
<template #footer>
<div class="flex justify-end">
<el-button @click="cloudBuildError('local')" type="primary">{{ t("upgrade.localBuild") }}</el-button>
<el-button @click="cloudBuildError('retry')" type="primary">{{ t("upgrade.cloudBuild") }}{{ retrySecond }}S</el-button>
<el-button @click="cloudBuildError('rollback')" type="primary">{{ t("upgrade.rollback") }}</el-button>
</div>
</template>
</el-dialog>
@ -116,25 +219,51 @@ import { ref, h, watch } from 'vue'
import { t } from '@/lang'
import { getVersions } from '@/app/api/auth'
import { getFrameworkNewVersion } from '@/app/api/module'
import { getUpgradeContent, getUpgradeTask, upgradeAddon, executeUpgrade, preUpgradeCheck, clearUpgradeTask } from '@/app/api/upgrade'
import {
getUpgradeContent,
getUpgradeTask,
upgradeAddon,
executeUpgrade,
preUpgradeCheck,
clearUpgradeTask, upgradeUserOperate
} from '@/app/api/upgrade'
import { Terminal, TerminalFlash } from 'vue-web-terminal'
import 'vue-web-terminal/lib/theme/dark.css'
import { AnyObject } from '@/types/global'
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
import Storage from '@/utils/storage'
import { useRouter } from 'vue-router'
const router = useRouter()
const showDialog = ref<boolean>(false)
const upgradeContent = ref<null | AnyObject>(null)
const isAllowUpgrade = ref(true) //
const upgradeTask = ref<null | AnyObject>(null)
const active = ref('content')
const active = ref('upgrade')
const step = ref(1)
const upgradeCheck = ref<null | AnyObject>(null)
const uploading = ref(false)
const loading = ref(false)
const terminalRef: any = ref(null)
const emits = defineEmits(['complete', 'cloudbuild'])
const upgradeTipsShowDialog = ref<boolean>(false)
let upgradeLog: any = []
let errorLog: any = []
const cloudBuildErrorTipsShowDialog = ref<boolean>(false)
const retrySecond = ref(30)
let retrySecondInterval: any = null
const isNeedBackup = ref(true)
// backupCode backupSql
const excludeSteps: any = ref([
{
name: '备份源码',
code: 'backupCode'
},
{
name: '备份数据库',
code: 'backupSql'
}
])
/**
* 查询升级任务
*/
@ -142,6 +271,24 @@ const getUpgradeTaskFn = () => {
getUpgradeTask().then(({ data }) => {
if (!data) return
if (!upgradeContent.value) {
upgradeContent.value = data.upgrade_content
let upgradeCount = 0
let failUpgradeCount = 0
for (let i = 0; i < upgradeContent.value.content.length; i++) {
if (upgradeContent.value.content[i].version_list.length) {
upgradeCount++
} else {
failUpgradeCount++
}
}
if (upgradeContent.value.content.length == upgradeCount) {
isAllowUpgrade.value = true
} else if (upgradeContent.value.content.length == failUpgradeCount) {
isAllowUpgrade.value = false
}
}
//
if (!showDialog.value) {
showElNotification()
@ -151,15 +298,18 @@ const getUpgradeTaskFn = () => {
terminalRef.value.execute('clear')
terminalRef.value.execute('开始升级')
}
data.log.forEach(item => {
upgradeTask.value = data
data.log.forEach((item) => {
if (!upgradeLog.includes(item)) {
terminalRef.value.pushMessage({content: `正在执行:${item}`})
terminalRef.value.pushMessage({ content: `${item}` })
upgradeLog.push(item)
}
})
//
if (data.error) {
data.error.forEach(item => {
data.error.forEach((item) => {
if (!errorLog.includes(item)) {
terminalRef.value.pushMessage({ content: item, class: 'error' })
errorLog.push(item)
@ -168,29 +318,46 @@ const getUpgradeTaskFn = () => {
}
//
if (data.step == 'restoreComplete') {
flashInterval && clearInterval(flashInterval)
return
}
//
if (data.step == 'upgradeComplete') {
active.value = 'complete'
numberOfSteps.value = 4
notificationEl && notificationEl.close()
emits('complete')
clearUpgradeTask()
return
}
upgradeTask.value = data
numberOfSteps.value = 2
active.value = 'upgrade'
executeUpgradeFn()
}).catch()
})
}
getUpgradeTaskFn()
const executeUpgradeFn = () => {
executeUpgrade().then(() => {
getUpgradeTaskFn()
}).catch()
}).catch((err) => {
if (err.message.indexOf('队列') != -1) {
retrySecond.value = 30
retrySecondInterval = setInterval(() => {
retrySecond.value--
if (retrySecond.value == 0) {
cloudBuildError('retry')
}
}, 1000)
cloudBuildErrorTipsShowDialog.value = true
} else {
ElMessage({ message: err.message, type: 'error' })
}
})
}
let notificationEl : any = null
let notificationEl: any = null
/**
* 升级中任务提示
*/
@ -198,10 +365,10 @@ const showElNotification = () => {
notificationEl = ElNotification.success({
title: t('warning'),
dangerouslyUseHTMLString: true,
message: h('div', {}, [
t('upgrade.upgradingTips'),
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('upgrade.clickView')])
]),
message: h('div', {}, [t('upgrade.upgradingTips'), h('span', {
class: 'text-primary cursor-pointer',
onClick: elNotificationClick
}, [t('upgrade.clickView')])]),
duration: 0,
showClose: false
})
@ -209,16 +376,18 @@ const showElNotification = () => {
const elNotificationClick = () => {
showDialog.value = true
active.value = 'upgrade'
getUpgradeTaskFn()
step.value = 2
numberOfSteps.value = 3
active.value = 'upgrade'
notificationEl && notificationEl.close()
}
const frameworkVersion = ref('')
getVersions().then(res => {
getVersions().then((res) => {
frameworkVersion.value = res.data.version.version
})
const newFrameworkVersion = ref("")
const newFrameworkVersion = ref('')
getFrameworkNewVersion().then(({ data }) => {
newFrameworkVersion.value = data.last_version
})
@ -226,44 +395,90 @@ getFrameworkNewVersion().then(({ data }) => {
/**
* 执行升级
*/
const is_pass = ref(false)
const repeat = ref(false)
const readyLoading = ref(false)
const handleUpgrade = async () => {
if (uploading.value) return
uploading.value = true
if (repeat.value) return
repeat.value = true
readyLoading.value = true
const appKey = upgradeContent.value?.app.app_key != 'niucloud-admin' ? upgradeContent.value?.app.app_key : ''
const appKey = upgradeContent.value?.upgrade_apps.join(',') != 'niucloud-admin' ? upgradeContent.value?.upgrade_apps.join(',') : ''
await preUpgradeCheck(appKey).then(async({ data }) => {
if (data.is_pass) {
await upgradeAddon(appKey).then(() => {
getUpgradeTaskFn()
}).catch(() => {
uploading.value = false
})
} else {
upgradeCheck.value = data
}
}).catch()
if (uploading.value) active.value = 'upgrade'
await preUpgradeCheck(appKey).then(async ({ data }) => {
upgradeCheck.value = data
is_pass.value = data.is_pass
active.value = 'upgrade'
!upgradeTask.value ? (numberOfSteps.value = 0) : numberOfSteps.value
upgradeTipsShowDialog.value = false
showDialog.value = true
repeat.value = false
readyLoading.value = false
}).catch(() => {
repeat.value = false
readyLoading.value = false
})
}
const open = (addonKey: string = '') => {
const upgradeAddonFn = () => {
if (!is_pass.value) return
if (loading.value) return
loading.value = true
const appKey = upgradeContent.value?.upgrade_apps.join(',') != 'niucloud-admin' ? upgradeContent.value?.upgrade_apps.join(',') : ''
upgradeAddon(appKey, { is_need_backup: isNeedBackup.value }).then(() => {
getUpgradeTaskFn()
}).catch(() => {
loading.value = false
})
}
const open = (addonKey: string = '', callback = null) => {
if (upgradeTask.value) {
ElMessage({ message: '已有正在执行中的升级任务', type: 'error' })
showDialog.value = true
step.value = 2
numberOfSteps.value = 3
active.value = 'upgrade'
if (callback) callback()
} else {
if (addonKey && frameworkVersion.value != newFrameworkVersion.value) {
ElMessage({ message: '存在新版本框架,请先升级框架', type: 'error' })
ElMessage({ message: "存在新版本框架,请先升级框架", type: "error" })
if (callback) callback()
return
}
if (loading.value) return
loading.value = true
getUpgradeContent(addonKey).then(({ data }) => {
loading.value = false
upgradeContent.value = data
let upgradeCount = 0
let failUpgradeCount = 0
for (let i = 0; i < upgradeContent.value.content.length; i++) {
if (upgradeContent.value.content[i].version_list.length) {
upgradeCount++
} else {
failUpgradeCount++
}
}
if (upgradeContent.value.content.length == upgradeCount) {
isAllowUpgrade.value = true
} else if (upgradeContent.value.content.length == failUpgradeCount) {
isAllowUpgrade.value = false
}
if (Storage.get('upgradeTipsLock')) {
showDialog.value = true
handleUpgrade()
} else {
upgradeTipsShowDialog.value = true
}
}).catch()
if (callback) callback()
}).catch(() => {
loading.value = false
if (callback) callback()
})
}
}
@ -283,10 +498,10 @@ const onExecCmd = (key, command, success, failed, name) => {
}
const makeIterator = (array: string[]) => {
var nextIndex = 0
let nextIndex = 0
return {
next() {
if ((nextIndex + 1) == array.length) {
next () {
if (nextIndex + 1 == array.length) {
nextIndex = 0
}
return { value: array[nextIndex++] }
@ -295,45 +510,71 @@ const makeIterator = (array: string[]) => {
}
const dialogClose = (done: () => {}) => {
if (active.value == 'upgrade' && upgradeTask.value && !upgradeTask.value.error) {
ElMessageBox.confirm(
t('upgrade.showDialogCloseTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
if (active.value == 'upgrade' && upgradeTask.value && ['upgradeComplete', 'restoreComplete'].includes(upgradeTask.value.step) === false) {
ElMessageBox.confirm(t('upgrade.showDialogCloseTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}).then(() => {
done()
}).catch(() => { })
})
} else {
done()
}
}
watch(() => showDialog.value, () => {
if (!showDialog.value) {
clearUpgradeTaskFn()
watch(
() => showDialog.value,
() => {
if (!showDialog.value) {
clearUpgradeTaskFn()
}
}
})
)
const clearUpgradeTaskFn = () => {
active.value = 'content'
uploading.value = false
active.value = 'upgrade'
loading.value = false
upgradeTask.value = null
upgradeLog = []
errorLog = []
numberOfSteps.value = 0
flashInterval && clearInterval(flashInterval)
clearUpgradeTask().then(() => {}).catch()
retrySecondInterval && clearInterval(retrySecondInterval)
isNeedBackup.value = true
step.value = 1
clearUpgradeTask().then(() => {
})
}
/**
* 云编译
* 云编译队列不足操作
* @param event
*/
const handleCloudBuild = () => {
showDialog.value = false
emits('cloudbuild')
const cloudBuildError = (event: string) => {
cloudBuildErrorTipsShowDialog.value = false
switch (event) {
case 'local':
upgradeUserOperate(event).then(() => {
getUpgradeTaskFn()
})
break
case 'retry':
executeUpgradeFn()
retrySecondInterval && clearInterval(retrySecondInterval)
break
case 'rollback':
upgradeUserOperate(event).then(() => {
getUpgradeTaskFn()
})
break
}
}
const timeSplit = (str: string) => {
const [date, time] = str.split(' ')
const [hours, minutes] = time.split(':')
return [date, `${hours}:${minutes}`]
}
const upgradeTipsConfirm = (isLock: boolean = false) => {
@ -341,9 +582,19 @@ const upgradeTipsConfirm = (isLock: boolean = false) => {
upgradeTipsShowDialog.value = false
!isLock && (showDialog.value = true)
}
const activeName = ref(0)
const numberOfSteps = ref(0)
const toBackupRecord = () => {
const routeUrl = router.resolve({
path: '/tools/backup_records'
})
window.open(routeUrl.href, '_blank')
}
defineExpose({
open
open,
loading
})
</script>
@ -351,7 +602,187 @@ defineExpose({
.table-head-bg {
background-color: var(--el-table-header-bg-color);
}
:deep(.terminal .t-log-box span) {
white-space: pre-wrap;
}
::v-deep .number-of-steps {
.el-step__line {
margin: 0 25px;
background: #dddddd;
}
.el-step__head {
margin-top: 10px;
}
.is-success {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
.el-step__icon {
background: var(--el-color-primary);
box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
i {
color: #fff;
}
}
.el-step__line {
margin: 0 25px;
background: var(--el-color-primary);
}
}
.is-process {
color: var(--el-color-primary);
font-weight: inherit;
// font-size: 18px;
.el-step__icon {
padding: 10px;
border: 1px solid var(--el-color-primary);
box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
}
}
.is-wait {
color: #333;
}
}
</style>
<style scoped>
.el-timeline-item {
min-height: 100px;
}
.el-timeline-item >>> .el-timeline-item__node--normal {
left: 117px;
width: 18px;
height: 18px;
background: rgba(25, 103, 249, 0.12) !important;
border-radius: 50%;
/* 创建圆形 */
position: relative;
/* 用于定位伪元素 */
}
.el-timeline-item >>> .el-timeline-item__node--normal::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 8px;
/* 中心圆直径 */
height: 8px;
background-color: var(--el-color-primary);
/* 中心圆颜色 */
border-radius: 50%;
/* 中心圆为圆形 */
transform: translate(-50%, -50%);
/* 居中显示 */
}
.el-timeline-item >>> .el-timeline-item__tail {
left: 125px;
border-left-color: #dddddd;
border-left-style: dashed;
margin: 12px 0;
margin-top: 24px;
height: calc(100% - 24px - 12px);
}
.time-dialog-wrap >>> .el-dialog__header {
padding: 10px 20px;
height: 25px;
line-height: 25px;
text-align: left;
background: #fff;
border-bottom: solid 1px #e4e7ed;
}
.time-dialog-wrap >>> .el-card__body {
padding: 8px;
}
.time-dialog-wrap >>> .el-card.is-always-shadow {
box-shadow: none;
}
.time-dialog-wrap >>> .el-dialog__headerbtn .el-dialog__close {
color: #666;
}
.time-dialog-wrap >>> .el-dialog__headerbtn:hover .el-dialog__close {
color: #666;
}
.time-dialog-wrap >>> .el-dialog__headerbtn {
top: 14px;
}
.time-dialog-wrap >>> .el-collapse {
margin-left: 119px;
border: none;
margin-top: -22px;
}
.time-dialog-wrap >>> .el-collapse-item__header {
border: none;
line-height: 25px;
height: 25px;
position: relative;
z-index: 999;
}
.time-dialog-wrap >>> .el-collapse-item__wrap {
border: none;
}
.time-dialog-wrap >>> .el-collapse-item__content {
margin-top: 15px;
padding-bottom: 0 !important;
}
.time-dialog-wrap >>> .el-timeline-item__node--01 {
width: 18px !important;
height: 18px !important;
left: 117px !important;
}
</style>
<style>
.time-dialog-wrap .el-dialog {
margin: 0 auto !important;
max-height: 90%;
overflow: hidden;
top: 5%;
}
.time-dialog-wrap .el-dialog {
margin: 0 auto !important;
height: 65%;
overflow: hidden;
top: 10%;
}
.time-dialog-wrap .el-dialog__body {
position: absolute;
left: 0;
top: 46px;
bottom: 0px;
right: 0;
z-index: 1;
overflow: hidden;
overflow-y: auto;
padding: 10px 20px 0 0;
}
.time-dialog-wrap .el-timeline-item__wrapper {
top: -20px !important;
}
</style>

View File

@ -25,11 +25,9 @@ import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { deepClone } from '@/utils/common'
import { getUserInfo, setUserInfo } from '@/app/api/personal'
import { useRouter } from 'vue-router'
import useUserStore from '@/stores/modules/user'
const userStore = useUserStore()
const router = useRouter()
//
const saveInfo: any = reactive({})
const formRef = ref<FormInstance>()

View File

@ -1,25 +0,0 @@
{
"todayData": "today's data",
"memberNumb": "number of member",
"numberOfSites": "number of sites",
"numberOfVisitors": "number of visitors",
"commonlyUsedFunction": "commonly used function",
"articleList": "article list",
"memberManagement": "member management",
"balanceAccount": "balance account",
"administrator": "administrator",
"WebDecoration": "website decoration",
"accessMessage": "access message",
"memberDistribution": "membership distribution",
"systemInfo": "system environment",
"os": "os",
"phpVersions": "php version number",
"productionEnvironment": "production environment",
"versionsInfo": "version information",
"versions": "current version",
"frame": "framework based",
"channel": "access channel",
"serviceSupport": "service support",
"officialWbsite": "official website",
"pageView": "page view"
}

View File

@ -1,19 +1,19 @@
{
"companyName": "授权主体",
"siteAddress": "授权域名",
"contactName": "授权联系人",
"authCode": "授权码",
"authSecret": "授权秘钥",
"createTime": "授权时间",
"expireTime": "到期时间",
"authApp": "授权应用",
"authAppKey": "应用标识",
"siteAddressTips": "授权域名不匹配",
"authCodePlaceholder": "请输入授权码",
"authSecretPlaceholder": "请输入授权秘钥",
"updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
"versionTips":"已经升级到最新版本"
"companyName": "授权主体",
"siteAddress": "授权域名",
"contactName": "授权联系人",
"authCode": "授权码",
"authSecret": "授权秘钥",
"createTime": "授权时间",
"expireTime": "到期时间",
"authApp": "授权应用",
"authAppKey": "应用标识",
"siteAddressTips": "授权域名不匹配",
"authCodePlaceholder": "请输入授权码",
"authSecretPlaceholder": "请输入授权秘钥",
"updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
"versionTips": "已经升级到最新版本"
}

View File

@ -0,0 +1,18 @@
{
"companyName": "授权主体",
"siteAddress": "授权域名",
"contactName": "授权联系人",
"authCode": "授权码",
"authSecret": "授权秘钥",
"createTime": "授权时间",
"expireTime": "到期时间",
"authApp": "授权应用",
"authAppKey": "应用标识",
"siteAddressTips": "授权域名不匹配",
"authCodePlaceholder": "请输入授权码",
"authSecretPlaceholder": "请输入授权秘钥",
"updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"versionTips": "已经升级到最新版本"
}

View File

@ -50,7 +50,7 @@
"cashOutInfo":"收款方信息",
"transferCode":"收款码",
"realname":"真实姓名",
"account":"账号",
"account":"收款账号",
"bankRealname":"持卡人姓名",
"remark":"备注",
"remarkPlaceholder":"请输入备注",

View File

@ -1,52 +0,0 @@
{
"dataSummarize": "数据概况",
"todayData": "今日数据",
"memberNumb": "新增会员数",
"orderMoney": "订单金额",
"numberOfVisitors": "今日访客数",
"commonlyUsedFunction": "常用功能",
"articleList": "文章列表",
"memberManagement": "会员管理",
"balanceAccount": "余额账户",
"administrator": "站点用户",
"WebDecoration": "网站装修",
"accessMessage": "访问消息",
"memberDistribution": "会员分布",
"systemInfo": "系统环境",
"os": "操作系统:",
"phpVersions": "PHP版本号:",
"productionEnvironment": "生产环境:",
"versionsInfo": "版本信息",
"versions": "当前版本",
"frame": "基于框架",
"channel": "获取渠道",
"serviceSupport": "官方客服",
"officialWbsite": "官网",
"pageView": "访问量",
"siteInfo":"站点信息",
"siteName":"站点名称",
"groupName":"站点套餐",
"expireTime":"过期时间",
"permanent":"永久",
"statusName":"站点状态",
"newSiteSum": "新增站点数",
"total": "总计",
"newMemberSum": "新增用户数",
"siteList": "站点列表",
"sitePackage": "站点套餐",
"newSite": "新增站点",
"appMarketplace": "应用市场",
"siteDistribution": "站点分布",
"addUser": "新增用户",
"normalSiteSum": "正常站点(个)",
"weekExpireSiteCount":"即将到期站点(个)",
"expireSiteSum": "过期站点(个)",
"noInstallAppSun": "未安装应用(个)",
"installAppSun": "已安装应用(个)",
"officialAccount": "Niucloud官方公众号",
"officialAccountDesc": "微信扫码关注",
"WeCom": "客服二维码",
"WeComDesc": "扫码联系客服",
"tel": "服务热线:",
"newVersion": "最新版本"
}

View File

@ -1,67 +0,0 @@
{
"todayData": "实时概况",
"memberNumb": "新增会员数(人)",
"orderMoney": "订单金额(元)",
"numberOfSites": "站点数量",
"numberOfVisitors": "今日访客数(人)",
"commonlyUsedFunction": "常用功能",
"articleList": "文章列表",
"memberManagement": "会员管理",
"balanceAccount": "余额账户",
"administrator": "管理员",
"WebDecoration": "网站装修",
"accessMessage": "访问消息",
"memberDistribution": "会员分布",
"systemInfo": "系统环境",
"os": "操作系统",
"phpVersions": "PHP版本号",
"productionEnvironment": "生产环境",
"versionsInfo": "版本信息",
"versions": "当前版本",
"frame": "基于框架",
"channel": "获取渠道",
"serviceSupport": "服务支持",
"officialWbsite": "官网",
"pageView": "访客数(人)",
"siteInfo":"站点信息",
"siteName":"站点名称",
"groupName":"站点套餐",
"expireTime":"过期时间",
"permanent":"永久",
"statusName":"站点状态",
"orderNumber": "订单数(笔)",
"wechatCode": "公众号二维码",
"wechatCodeDesc": "微信扫码关注",
"enterpriseWechatCode": "客服二维码",
"enterpriseWechatCodeDesc": "扫码联系客服",
"tel": "服务热线:",
"message": "请联系客服",
"messageTitle": "提示",
"accumulative":"累计",
"officialAccount": "Niucloud官方公众号",
"officialAccountDesc": "微信扫码关注",
"WeCom": "添加企业微信群",
"path": "地址",
"menuName": "名称",
"menuNamePlaceholder": "模版名称",
"menuBgColor": "背景颜色",
"menuImg": "选择图标",
"menuDesc": "描述",
"addShortcutMenu": "添加快捷模版",
"appTemplate": "应用模块",
"siteType": "站点类型",
"periodTime": "有效期",
"renew": "续费",
"selectModel": "选择模块",
"addMenu": "添加模块",
"shortcutLink": "模版",
"emptyMenu": "暂无快捷模块",
"select": "选择",
"custom": "自定义",
"accessSite": "访问站点",
"pathSelect": "选择模块",
"bgColorPlaceholder": "请选择背景色",
"iconPlaceholder": "请选择图标",
"pathPlaceholder": "请选择链接",
"descPlaceholder": "输入描述语…"
}

View File

@ -1,76 +1,79 @@
{
"search":"搜索应用名称",
"appName":"应用名/版本信息",
"introduction":"简介",
"type":"类型",
"app":"应用",
"addon":"插件",
"noPlug":"暂无应用",
"install":"安装",
"unload":"卸载",
"installLabel":"已安装",
"uninstalledLabel":"未安装",
"version":"版本",
"title":"名称",
"desc":"简介",
"plugDetail": "插件信息",
"author": "作者",
"detail":"详情",
"addonInstall": "插件安装",
"dirPermission": "目录读写权限",
"path": "路径",
"demand": "要求",
"readable": "可读",
"write": "可写",
"packageManageTool": "包管理工具",
"name": "名称",
"addonInstallSuccess": "插件安装成功",
"envCheck": "环境检查",
"installProgress": "安装进度",
"installComplete": "安装完成",
"localAppText":"插件管理",
"marketAppText":"官方市场",
"installShowDialogCloseTips": "安装任务尚未完成,关闭将取消安装任务,是否要继续关闭?",
"marketDevelopMessage":"官方市场正在开发中!",
"jobError": "任务队列未启动 请在服务端源码部署目录打开终端执行 php think queue:listen",
"conflictFiles": "冲突文件",
"process": "启动进程",
"open": "开启",
"down": "下载",
"installDown":"未安装(重新下载)",
"unloadDown":"已安装(重新下载)",
"addonVersion": "插件版本",
"versionCode": "版本号",
"createTime": "发布时间",
"buyLabel": "已购买",
"installTips": "安装后需手动更新插件引用的依赖和编译各个端口的前端源码",
"localInstall":"本地安装",
"cloudInstall": "一键云安装",
"cloudInstallTips": "云安装可实现一键安装,安装后无需手动更新依赖和编译前端源码",
"installingTips": "有插件正在安装中请等待安装完成之后再进行其他操作,点击查看",
"installPercent": "安装进度",
"downloading": "下载中",
"authTips": "云安装需先绑定授权码如果已有授权请先进行绑定没有授权可到niucloud官网购买云服务之后再进行操作",
"toBind": "绑定授权",
"toNiucloud": "去niucloud官网",
"descriptionLeft": "暂无任何应用,马上去",
"buyDescriptionLeft": "您还没有购买过应用,马上去",
"link": "官方应用市场",
"descriptionRight": "逛逛",
"installed-empty": "暂未安装任何应用",
"siteAddressTips": "授权域名不匹配",
"authCodePlaceholder": "请输入授权码",
"authSecretPlaceholder": "请输入授权秘钥",
"updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"addonUninstall": "插件卸载",
"appIdentification":"应用标识",
"tipText":"标识指开发应用或插件的文件夹名称",
"uninstallTips": "是否要卸载该插件?",
"upgrade": "升级",
"newVersion": "最新版本",
"cloudBuild": "云编译",
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
"deleteAddonTips": "删除插件会把插件目录连同文件全部删除,确定要删除吗?"
"search": "搜索应用名称",
"appName": "应用名/版本信息",
"introduction": "简介",
"type": "类型",
"app": "应用",
"addon": "插件",
"noPlug": "暂无应用",
"install": "安装",
"unload": "卸载",
"installLabel": "已安装",
"uninstalledLabel": "未安装",
"version": "版本",
"title": "名称",
"desc": "简介",
"plugDetail": "插件信息",
"author": "作者",
"detail": "详情",
"addonInstall": "插件安装",
"dirPermission": "目录读写权限",
"path": "路径",
"demand": "要求",
"readable": "可读",
"write": "可写",
"packageManageTool": "包管理工具",
"name": "名称",
"addonInstallSuccess": "插件安装成功",
"envCheck": "环境检查",
"installProgress": "安装进度",
"installComplete": "安装完成",
"localAppText": "插件管理",
"marketAppText": "官方市场",
"installShowDialogCloseTips": "安装任务尚未完成,关闭将取消安装任务,是否要继续关闭?",
"marketDevelopMessage": "官方市场正在开发中!",
"jobError": "任务队列未启动 请在服务端源码部署目录打开终端执行 php think queue:listen",
"conflictFiles": "冲突文件",
"process": "启动进程",
"open": "开启",
"down": "下载",
"installDown": "未安装(重新下载)",
"unloadDown": "已安装(重新下载)",
"addonVersion": "插件版本",
"versionCode": "版本号",
"createTime": "发布时间",
"buyLabel": "已购买",
"recentlyUpdated": "最近更新",
"installTips": "安装后需手动更新插件引用的依赖和编译各个端口的前端源码",
"localInstall": "本地安装",
"cloudInstall": "一键云安装",
"cloudInstallTips": "云安装可实现一键安装,安装后无需手动更新依赖和编译前端源码",
"installingTips": "有插件正在安装中请等待安装完成之后再进行其他操作,点击查看",
"installPercent": "安装进度",
"downloading": "下载中",
"authTips": "云安装需先绑定授权码如果已有授权请先进行绑定没有授权可到niucloud官网购买云服务之后再进行操作",
"toBind": "绑定授权",
"toNiucloud": "去niucloud官网",
"descriptionLeft": "暂无任何应用,马上去",
"buyDescriptionLeft": "您还没有购买过应用,马上去",
"link": "官方应用市场",
"descriptionRight": "逛逛",
"installed-empty": "暂未安装任何应用",
"recentlyUpdatedEmpty": "暂无最近更新应用",
"siteAddressTips": "授权域名不匹配",
"authCodePlaceholder": "请输入授权码",
"authSecretPlaceholder": "请输入授权秘钥",
"updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"addonUninstall": "插件卸载",
"appIdentification": "应用标识",
"tipText": "标识指开发应用或插件的文件夹名称",
"uninstallTips": "是否要卸载该插件?",
"upgrade": "一键升级",
"newVersion": "最新版本",
"cloudBuild": "云编译",
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
"deleteAddonTips": "删除插件会把插件目录连同文件全部删除,确定要删除吗?",
"batchUpgrade": "批量升级"
}

View File

@ -3,7 +3,6 @@
"logging": "登录中",
"platform": "管理端",
"login" : "登录",
"siteLogin": "站点登录",
"adminLogin": "平台登录",
"userPlaceholder": "请输入您的账号",
"passwordPlaceholder": "请输入您的密码",

View File

@ -29,6 +29,7 @@
"continueSign": "连签天数",
"continueSignFormatError": "连签天数格式错误",
"continueSignBerweenDays": "连签天数为2-365天",
"continueSignMustLessThanSignPeriod": "连签天数不能大于签到周期",
"receiveLimit": "领取限制",
"noLimit": "不限制",
"everyOneLimit": "每人限领",

View File

@ -1,31 +1,31 @@
{
"hotelVerify": "订单核销",
"verifyRecord": "核销记录",
"verifyTime": "核销时间",
"orderNo": "订单编号",
"orderNoPlaceholder": "请输入订单编号",
"OrderInfo": "订单信息",
"buyerInfo": "购买人信息",
"verifyCode": "核销码",
"verifyCodePlaceholder": "请输入核销码",
"verifyer": "核销人",
"startDate": "开始时间",
"endDate": "结束时间",
"mobile": "联系方式",
"searchValueEmptyTips": "请输入搜索内容",
"verify": "核销",
"buyInfo": "预订信息",
"orderRefunding": "该订单正在维权中不能进行核销",
"verifyTips": "是否要核销该订单?",
"toOrder": "查看订单",
"verifyType": "核销类型",
"verifyTypePlaceholder": "请选择核销类型",
"verifier": "核销员",
"createTime":"添加时间",
"addVerifier":"添加核销员",
"verifierDeleteTips":"确定要删除该核销员吗?",
"memberInfo": "会员信息",
"memberIdPlaceholder": "请选择会员",
"member": "会员",
"searchPlaceholder": "请输入会员昵称搜索"
"hotelVerify": "订单核销",
"verifyRecord": "核销记录",
"verifyTime": "核销时间",
"orderNo": "订单编号",
"orderNoPlaceholder": "请输入订单编号",
"OrderInfo": "订单信息",
"buyerInfo": "购买人信息",
"verifyCode": "核销码",
"verifyCodePlaceholder": "请输入核销码",
"verifyer": "核销人",
"startDate": "开始时间",
"endDate": "结束时间",
"mobile": "联系方式",
"searchValueEmptyTips": "请输入搜索内容",
"verify": "核销",
"buyInfo": "预订信息",
"orderRefunding": "该订单正在售后中不能进行核销",
"verifyTips": "是否要核销该订单?",
"toOrder": "查看订单",
"verifyType": "核销类型",
"verifyTypePlaceholder": "请选择核销类型",
"verifier": "核销员",
"createTime": "添加时间",
"addVerifier": "添加核销员",
"verifierDeleteTips": "确定要删除该核销员吗?",
"memberInfo": "会员信息",
"memberIdPlaceholder": "请选择会员",
"member": "会员",
"searchPlaceholder": "请输入会员编号/昵称/手机号"
}

View File

@ -1,32 +1,32 @@
{
"hotelVerify": "订单核销",
"verifyRecord": "核销记录",
"verifyTime": "核销时间",
"orderNo": "订单编号",
"orderNoPlaceholder": "请输入订单编号",
"verifyInfo": "核销信息",
"buyerInfo": "购买人信息",
"verifyCode": "核销码",
"verifyCodePlaceholder": "请输入核销码",
"verifyer": "核销人",
"startDate": "开始时间",
"endDate": "结束时间",
"mobile": "联系方式",
"searchValueEmptyTips": "请输入搜索内容",
"verify": "核销",
"buyInfo": "预订信息",
"orderRefunding": "该订单正在维权中不能进行核销",
"verifyTips": "是否要核销该订单?",
"toOrder": "查看订单",
"verifyType": "核销类型",
"verifyTypePlaceholder": "请选择核销类型",
"verifier": "核销员",
"verifierPlaceholder": "请选择核销员",
"createTime":"添加时间",
"addVerifier":"添加核销员",
"verifierDeleteTips":"确定要删除该核销员吗?",
"memberInfo": "会员信息",
"memberIdPlaceholder": "请选择会员",
"member": "会员",
"searchPlaceholder": "请输入会员昵称搜索"
"hotelVerify": "订单核销",
"verifyRecord": "核销记录",
"verifyTime": "核销时间",
"orderNo": "订单编号",
"orderNoPlaceholder": "请输入订单编号",
"verifyInfo": "核销信息",
"buyerInfo": "购买人信息",
"verifyCode": "核销码",
"verifyCodePlaceholder": "请输入核销码",
"verifyer": "核销人",
"startDate": "开始时间",
"endDate": "结束时间",
"mobile": "联系方式",
"searchValueEmptyTips": "请输入搜索内容",
"verify": "核销",
"buyInfo": "预订信息",
"orderRefunding": "该订单正在售后中不能进行核销",
"verifyTips": "是否要核销该订单?",
"toOrder": "查看订单",
"verifyType": "核销类型",
"verifyTypePlaceholder": "请选择核销类型",
"verifier": "核销员",
"verifierPlaceholder": "请选择核销员",
"createTime": "添加时间",
"addVerifier": "添加核销员",
"verifierDeleteTips": "确定要删除该核销员吗?",
"memberInfo": "会员信息",
"memberIdPlaceholder": "请选择会员",
"member": "会员",
"searchPlaceholder": "请输入会员昵称搜索"
}

View File

@ -3,6 +3,6 @@
"type": "协议类型",
"titlePlaceholder": "请输入协议标题",
"contentPlaceholder": "请填写协议内容",
"contentMaxTips": "协议内容字符数应在5100000之间",
"contentMaxTips": "协议内容字符数应在510000之间",
"content": "内容"
}

View File

@ -1,35 +1,35 @@
{
"websiteInfo":"后台设置",
"contactAddress":"联系地址",
"siteName": "站点名称",
"keywords": "网站关键字",
"logo": "长方形Logo",
"desc": "网站简介",
"province": "省",
"city": "市",
"district": "区/县",
"selectAddress": "请选择地址",
"address": "详细地址",
"phone": "客服电话",
"businessHours": "营业时间",
"contactAddressPlaceholder":"联系地址",
"siteNamePlaceholder": "站点名称",
"keywordsPlaceholder": "网站关键字",
"descPlaceholder": "网站简介",
"addressPlaceholder": "详细地址",
"phonePlaceholder": "客服电话",
"businessHoursPlaceholder": "营业时间",
"businessHoursTips": "例上午9:00-12:00下午2:00-6:00",
"frontEndInfo": "前台设置",
"frontEndName": "前台名称",
"frontEndNamePlaceholder": "请输入前台名称",
"frontEndLogo": "前台Logo",
"icon": "正方形Logo",
"serviceInformation": "服务信息",
"wechatCode": "公众号二维码",
"customerServiceCode": "客服二维码",
"contactsTel": "联系电话",
"contactsTelPlaceholder": "请输入联系电话",
"logoPlaceholder": "建议图片尺寸210*30像素图片格式jpg、png、jpeg。",
"iconPlaceholder": "建议图片尺寸100*100像素图片格式jpg、png、jpeg。"
"websiteInfo": "后台设置",
"contactAddress": "联系地址",
"siteName": "站点名称",
"keywords": "网站关键字",
"logo": "长方形Logo",
"desc": "网站简介",
"province": "省",
"city": "市",
"district": "区/县",
"selectAddress": "请选择地址",
"address": "详细地址",
"phone": "客服电话",
"businessHours": "营业时间",
"contactAddressPlaceholder": "联系地址",
"siteNamePlaceholder": "站点名称",
"keywordsPlaceholder": "网站关键字",
"descPlaceholder": "网站简介",
"addressPlaceholder": "详细地址",
"phonePlaceholder": "客服电话",
"businessHoursPlaceholder": "营业时间",
"businessHoursTips": "例上午9:00-12:00下午2:00-6:00",
"frontEndInfo": "前台设置",
"frontEndName": "前台名称",
"frontEndNamePlaceholder": "请输入前台名称",
"frontEndLogo": "前台Logo",
"icon": "正方形Logo",
"serviceInformation": "服务信息",
"wechatCode": "公众号二维码",
"customerServiceCode": "客服二维码",
"contactsTel": "联系电话",
"contactsTelPlaceholder": "请输入联系电话",
"logoPlaceholder": "建议图片尺寸210*30像素图片格式jpg、png、jpeg。",
"iconPlaceholder": "建议图片尺寸100*100像素图片格式jpg、png、jpeg。"
}

View File

@ -27,6 +27,8 @@
"appPublicCertPathTips": "上传appCertPublicKey文件",
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
"alipayRootCertPathTips": "上传alipayRootCert文件",
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景"
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景",
"transferTips":"注意:应微信方规定,在2025年1月15日前开通商家转账到零钱服务的商户号可正常使用转账功能,之后开通的不支持使用转账到零钱服务",
"wechatpayPublicCert": "微信支付公钥",
"wechatpayPublicCertId": "微信支付公钥ID"
}

View File

@ -0,0 +1,28 @@
{
"manualBackup": "手动备份",
"manualBackupTips": "即将开始手动备份您的源码和数据库,为确保备份过程中顺利进行以及数据的完整性,建议您暂停所有相关操作,避免因数据写入或修改导致备份不一致,是否确定继续?",
"id": "编号",
"content": "内容",
"currentVersion": "备份版本",
"contentPlaceholder": "请输入内容",
"batchDelete": "批量删除",
"backupDir": "备份路径",
"completeTime": "备份时间",
"restore": "恢复",
"restoreTips": "此操作将恢复备份的源码和数据库,并且覆盖当前数据。为避免意外损失,请确认已了解此操作的影响,并确保已备份重要信息,是否确定继续?",
"deleteTips": "删除记录将会同步删除其备份文件,确定要操作吗?",
"batchEmptySelectedTips": "请选择需要批量删除的记录",
"restoreTitle": "恢复备份",
"backupCompleteTips": "备份成功",
"restoreCompleteTips": "恢复成功",
"showDialogCloseTips": "任务尚未完成,关闭将会造成数据丢失或系统损坏的影响,是否要继续关闭?",
"manualBackupTitle": "手动备份",
"checkDirectoryPermissions": "检测目录权限",
"backupFiles": "备份文件",
"startUpgrade": "开始恢复",
"upgradeEnd": "恢复完成",
"startBackUp": "开始备份",
"backUpEnd": "备份完成",
"remark": "备注",
"remarkEmpty": "无"
}

View File

@ -0,0 +1,12 @@
{
"id": "编号",
"upgradeName": "内容",
"prevVersion": "前一版本",
"currentVersion": "版本",
"upgradeNamePlaceholder": "请输入内容",
"completeTime": "升级时间",
"status": "状态",
"failReason": "失败原因",
"batchDelete": "批量删除",
"deleteTips": "确定要删除吗?"
}

View File

@ -2,47 +2,31 @@
<!--授权信息-->
<div class="main-container">
<el-card class="box-card !border-none" shadow="never" v-if="!loading">
<div class="flex">
<div class="w-[450px] mr-[20px] p-[50px] bg-[var(--el-color-info-light-9)]">
<div class="flex items-center justify-between">
<span class="text-page-title">版本信息</span>
<div class="flex-1 w-0 flex justify-end">
<el-button class="text-[#4C4C4C] w-[78px] h-[32px] !bg-transparent" @click="getFrameworkVersionListFn" v-if="!newVersion || (newVersion && newVersion.version_no == versions)">检测更新</el-button>
<el-button class="text-[#4C4C4C] w-[78px] h-[32px]" type="primary" @click="handleUpgrade" v-else>一键升级</el-button>
<el-button class="text-[#4C4C4C] w-[78px] h-[32px]" type="primary" @click="handleCloudBuild" :loading="cloudBuildRef?.loading">云编译</el-button>
</div>
</div>
<div class="mt-[30px] flex items-center text-[14px] text-[#797979]">
<span>当前版本</span>
<span class="text-[26px] ml-[15px] mr-[10px] text-[#656668]">{{versions}}</span>
<em class="text-[12px]" v-if="!newVersion || (newVersion && newVersion.version_no == versions)">(当前已是最新版本)</em>
<em class="text-[12px] text-[red]" v-else>(最新版本{{ newVersion.version_no }})</em>
</div>
</div>
<div class="flex flex-1 justify-between items-center p-[50px] bg-[var(--el-color-info-light-9)]">
<div>
<div class="text-[#333] text-[18px]">授权信息</div>
<div class="ml-[50px] mt-[40px]">
<div class="flex flex-col">
<div class="flex flex-wrap items-center">
<p class="text-page-title mr-[20px]">授权信息</p>
<span class="text-[14px] text-[#666]">{{ authinfo.company_name || '--' }}</span>
<span class="mr-[6px] text-[14px] text-[#666666] w-[70px] text-right">授权公司</span>
<span class="text-[14px] text-[#333]">{{ authinfo.company_name || "--" }}</span>
</div>
<div class="mt-[46px] ml-[40px] flex flex-wrap">
<span class="text-[14px] mr-[84px]">授权域名<em class="ml-[12px] text-[12px]">{{ authinfo.site_address || '--' }}</em></span>
<span class="text-[14px] flex items-center">
<span>授权码</span>
<em class="ml-[12px] mr-[10px] text-[12px]">{{ authinfo.auth_code ? (isCheck ? authinfo.auth_code : hideAuthCode(authinfo.auth_code)) : '--' }}</em>
<el-icon v-if="!isCheck" @click="isCheck = !isCheck" class="text-[12px] cursor-pointer">
<div class="flex flex-wrap items-center mt-[20px]">
<span class="mr-[6px] text-[14px] text-[#666666] w-[70px] text-right">授权域名</span>
<span class="text-[14px] text-[#333]">{{ authinfo.site_address || "--" }}</span>
</div>
<div class="flex flex-wrap items-center mt-[20px]">
<span class="mr-[6px] text-[14px] text-[#666666] w-[70px] text-right">授权码</span>
<span class="text-[14px] text-[#333]">
<span class="mr-[10px]">{{ authinfo.auth_code ? (isCheck ? authinfo.auth_code : hideAuthCode(authinfo.auth_code)) : "--" }}</span>
<el-icon v-if="!isCheck" @click="isCheck = !isCheck" class="text-[12px] cursor-pointer text-[#4383F9]">
<View />
</el-icon>
<el-icon v-else @click="isCheck = !isCheck" class="text-[12px] cursor-pointer">
<Hide />
</el-icon>
<el-icon v-else @click="isCheck = !isCheck" class="text-[12px] cursor-pointer text-[#4383F9]"> <Hide /> </el-icon>
</span>
</div>
</div>
<div class="flex flex-1 flex-wrap justify-end relative">
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
<div class="mt-[60px] mb-[50px]">
<el-button class="w-[150px] !h-[46px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
<el-popover ref="getAuthCodeDialog" placement="bottom-start" :width="478" trigger="click" class="mt-[8px]">
<div class="px-[18px] py-[8px]">
<p class="leading-[32px] text-[14px]">您在官方应用市场购买任意一款应用即可获得授权码输入正确授权码认证通过后即可支持在线升级和其它相关服务</p>
@ -52,7 +36,7 @@
</div>
</div>
<template #reference>
<el-button class="w-[154px] !h-[48px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent" plain type="primary">如何获取授权码?</el-button>
<el-button class="w-[150px] !h-[46px] mt-[8px] !text-[var(--el-color-primary)] hover:!text-[var(--el-color-primary)] !bg-transparent" plain type="primary">如何获取授权码?</el-button>
</template>
</el-popover>
</div>
@ -70,13 +54,13 @@
</el-form-item>
</div>
<div class="text-sm mt-[10px] text-info">{{ t('authInfoTips') }}</div>
<div class="text-sm mt-[10px] text-info">{{ t("authInfoTips") }}</div>
<div class="mt-[20px]">
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t('confirm') }}</el-button>
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t("confirm") }}</el-button>
</div>
<div class="mt-[10px] text-right">
<el-button type="primary" link @click="market">{{ t('notHaveAuth') }}</el-button>
<el-button type="primary" link @click="market">{{ t("notHaveAuth") }}</el-button>
</div>
</el-card>
</el-form>
@ -84,73 +68,24 @@
</div>
</div>
</el-card>
<el-card class="box-card !border-none " shadow="never" v-if="!loading">
<div class="text-page-title mb-[20px]">历史版本</div>
<el-timeline>
<el-timeline-item :timestamp="item['release_time'] + ' 版本:' + item['version_no']" v-for="(item,index) in frameworkVersionList" type="primary" :hollow="true" placement="top" :key="index">
<div class="mt-[10px] p-[20px] bg-overlay rounded-md timeline-log-wrap whitespace-pre-wrap" v-if="item['upgrade_log']">
<div v-html="item['upgrade_log']"></div>
</div>
</el-timeline-item>
</el-timeline>
</el-card>
<upgrade ref="upgradeRef" />
<cloud-build ref="cloudBuildRef" />
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, computed } from 'vue'
import { t } from '@/lang'
import { getVersions } from '@/app/api/auth'
import { getInstallConfig } from '@/app/api/sys'
import { getAuthInfo, setAuthInfo, getFrameworkVersionList } from '@/app/api/module'
import { ElMessageBox, FormInstance, FormRules, ElMessage } from 'element-plus'
import Upgrade from '@/app/components/upgrade/index.vue'
import CloudBuild from '@/app/components/cloud-build/index.vue'
import { cloneDeep } from 'lodash-es'
import { reactive, ref } from "vue"
import { t } from "@/lang"
import { getVersions } from "@/app/api/auth"
import { getAuthInfo, setAuthInfo } from "@/app/api/module"
import { FormInstance, FormRules } from "element-plus"
import { cloneDeep } from "lodash-es"
const upgradeRef = ref<any>(null)
const cloudBuildRef = ref<any>(null)
const getAuthCodeDialog: Record<string, any> | null = ref(null)
const authCodeApproveDialog = ref(false)
const isCheck = ref(false)
const frameworkVersionList = ref([])
const installPhpConfig = ref(null)
getInstallConfig().then(({ data }) => {
installPhpConfig.value = data
}).catch()
const checkVersion = ref(false)
const getFrameworkVersionListFn = () => {
getFrameworkVersionList().then(({ data }) => {
frameworkVersionList.value = data
if (checkVersion.value) {
if (!newVersion.value || (newVersion.value && newVersion.value.version_no == versions.value)) {
ElMessage({
message: t('versionTips'),
type: 'success'
})
}
} else {
checkVersion.value = true
}
})
}
getFrameworkVersionListFn()
const newVersion:any = computed(() => {
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
})
const hideAuthCode = (res:any) => {
const hideAuthCode = (res: any) => {
const authCode = cloneDeep(res)
const data = authCode.slice(0, authCode.length / 2) + authCode.slice(authCode.length / 2, authCode.length - 1).replace(/./g, '*')
const data = authCode.slice(0, authCode.length / 2) + authCode.slice(authCode.length / 2, authCode.length - 1).replace(/./g, "*")
return data
}
@ -159,15 +94,15 @@ const authCodeApproveFn = () => {
}
interface AuthInfo {
company_name: string,
site_address: string,
company_name: string
site_address: string
auth_code: string
}
const authinfo = ref<AuthInfo>({
company_name: '',
site_address: '',
auth_code: ''
company_name: "",
site_address: "",
auth_code: ""
})
const loading = ref(true)
const saveLoading = ref(false)
@ -187,25 +122,21 @@ const checkAppMange = () => {
checkAppMange()
const formData = reactive<Record<string, string>>({
auth_code: '',
auth_secret: ''
auth_code: "",
auth_secret: ""
})
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({
auth_code: [
{ required: true, message: t('authCodePlaceholder'), trigger: 'blur' }
],
auth_secret: [
{ required: true, message: t('authSecretPlaceholder'), trigger: 'blur' }
]
auth_code: [{ required: true, message: t("authCodePlaceholder"), trigger: "blur" }],
auth_secret: [{ required: true, message: t("authSecretPlaceholder"), trigger: "blur" }]
})
const save = async (formEl: FormInstance | undefined) => {
const save = async(formEl: FormInstance | undefined) => {
if (saveLoading.value || !formEl) return
await formEl.validate(async (valid) => {
await formEl.validate(async(valid) => {
if (valid) {
saveLoading.value = true
@ -221,47 +152,16 @@ const save = async (formEl: FormInstance | undefined) => {
}
const market = () => {
window.open(installPhpConfig.value?.website_url)
window.open("https://www.niucloud.com/app")
}
const versions = ref('')
const versions = ref("")
const getVersionsInfo = () => {
getVersions().then(res => {
getVersions().then((res) => {
versions.value = res.data.version.version
})
}
getVersionsInfo()
/**
* 升级
*/
const handleUpgrade = () => {
if (!authinfo.value.auth_code) {
authCodeApproveFn()
return
}
upgradeRef.value?.open()
}
const handleCloudBuild = () => {
if (!authinfo.value.auth_code) {
authCodeApproveFn()
return
}
if (cloudBuildRef.value.cloudBuildTask) {
cloudBuildRef.value?.open()
return
}
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
cloudBuildRef.value?.open()
})
}
</script>
<style lang="scss" scoped></style>

View File

@ -59,10 +59,6 @@ const appList = ref<Record<string, any>[]>([])
const loading = ref(true)
const getAppList = async () => {
// const res = await getSiteAddons()
// appList.value = res.data
// loading.value = false
const res = await getShowApp()
appList.value = res.data
loading.value = false
@ -70,7 +66,6 @@ const getAppList = async () => {
getAppList()
const toLink = (item: any) => {
console.log('tol', item)
if (item.url) {
router.push(item.url)
} else {

View File

@ -0,0 +1,195 @@
<template>
<!--授权信息-->
<div class="main-container">
<el-card class="box-card !border-none" shadow="never" v-if="newVersion">
<div>
<div class="mx-[20px] my-[20px]">
<div class="title text-[18px]">版本信息</div>
<div class="text-[18px] text-center mb-[7px] mt-[40px]">系统当前版本v{{ version }}{{ versionCode }}</div>
<div class="text-center text-[#666] text-[14px]" v-if="!newVersion || (newVersion && newVersion.version_no == version)">
<span>当前已是最新版本无需升级</span>
<span class="text-[14px] text-primary ml-[10px] cursor-pointer" @click="openUpgrade">更新说明</span>
</div>
<div class="text-[#666] text-[14px] text-center" v-else>
当前系统最新版本为 <span class="text-[18px] text-[#FF4D01]">v{{ newVersion.version_no }}</span>
<span class="text-[14px] text-primary ml-[10px]" style="cursor: pointer" @click="openUpgrade">更新说明</span>
</div>
<div class="mt-[30px] flex justify-center items-center">
<el-button class="text-[#4C4C4C] w-[150px] !h-[44px]" type="primary" :loading="loading" @click="handleUpgrade" v-if="!(!newVersion || (newVersion && newVersion.version_no == version))">一键升级</el-button>
<el-button class="text-[#4C4C4C] w-[130px] !h-[44px]" @click="upgradeRecord">升级记录</el-button>
</div>
</div>
</div>
</el-card>
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
<el-form :model="formData" label-width="0" ref="formRef" :rules="formRules" class="page-form">
<el-card class="box-card !border-none" shadow="never">
<el-form-item prop="auth_code">
<el-input v-model.trim="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width" clearable size="large" />
</el-form-item>
<div class="mt-[20px]">
<el-form-item prop="auth_secret">
<el-input v-model.trim="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
</el-form-item>
</div>
<div class="text-sm mt-[10px] text-info">{{ t("authInfoTips") }}</div>
<div class="mt-[20px]">
<el-button type="primary" class="w-full" size="large" :loading="saveLoading" @click="save(formRef)">{{ t("confirm") }}</el-button>
</div>
<div class="mt-[10px] text-right">
<el-button type="primary" link @click="market">{{ t("notHaveAuth") }}</el-button>
</div>
</el-card>
</el-form>
</el-dialog>
<upgrade ref="upgradeRef" />
<upgrade-log ref="upgradeLogRef" />
</div>
</template>
<script lang="ts" setup>
import { ref, computed, reactive } from "vue"
import { t } from "@/lang"
import { getVersions } from "@/app/api/auth"
import { getAuthInfo, getFrameworkVersionList, setAuthInfo } from "@/app/api/module"
import { ElMessage, FormInstance, FormRules } from "element-plus"
import { useRouter } from "vue-router"
import Upgrade from "@/app/components/upgrade/index.vue"
import UpgradeLog from "@/app/components/upgrade-log/index.vue"
const upgradeRef = ref<any>(null)
const upgradeLogRef = ref<any>(null)
const authCodeApproveDialog = ref(false)
const frameworkVersionList = ref([])
const checkVersion = ref(false)
const formData = reactive<Record<string, string>>({
auth_code: '',
auth_secret: ''
})
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({
auth_code: [{ required: true, message: t('authCodePlaceholder'), trigger: 'blur' }],
auth_secret: [{ required: true, message: t('authSecretPlaceholder'), trigger: 'blur' }]
})
const saveLoading = ref(false)
const save = async(formEl: FormInstance | undefined) => {
if (saveLoading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
saveLoading.value = true
setAuthInfo(formData).then(() => {
saveLoading.value = false
checkAppMange()
}).catch(() => {
saveLoading.value = false
authCodeApproveDialog.value = false
})
}
})
}
const getFrameworkVersionListFn = () => {
getFrameworkVersionList().then(({ data }) => {
frameworkVersionList.value = data
if (checkVersion.value) {
if (!newVersion.value || (newVersion.value && newVersion.value.version_no == version.value)) {
ElMessage({
message: t('versionTips'),
type: 'success'
})
}
} else {
checkVersion.value = true
}
})
}
getFrameworkVersionListFn()
const newVersion: any = computed(() => {
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
})
const authCodeApproveFn = () => {
authCodeApproveDialog.value = true
}
const version = ref('')
const versionCode = ref('')
const getVersionsInfo = () => {
getVersions().then((res) => {
version.value = res.data.version.version
versionCode.value = res.data.version.code
})
}
getVersionsInfo()
interface AuthInfo {
company_name: string
site_address: string
auth_code: string
}
const authInfo = ref<AuthInfo>({
company_name: '',
site_address: '',
auth_code: ''
})
const repeat = ref(false)
const loading = ref(false)
/**
* 升级
*/
const handleUpgrade = () => {
if (!authInfo.value.auth_code) {
authCodeApproveFn()
return
}
if (repeat.value) return
repeat.value = true
loading.value = true
upgradeRef.value?.open('', () => {
repeat.value = false
loading.value = false;
})
}
const checkAppMange = () => {
getAuthInfo().then((res) => {
if (res.data.data && res.data.data.length != 0) {
authInfo.value = res.data.data
authCodeApproveDialog.value = false
}
}).catch(() => {
authCodeApproveDialog.value = false
})
}
checkAppMange()
const router = useRouter()
const upgradeRecord = () => {
router.push('/admin/tools/upgrade_records')
}
const openUpgrade = () => {
upgradeLogRef.value?.open()
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,5 +1,5 @@
<template>
<!--站点菜单-->
<!--平台菜单-->
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">

View File

@ -102,7 +102,6 @@ const router = useRouter()
const pageName = route.meta.title
const activeName = ref('/channel/aliapp')
const active = ref(2)
const qrCode = ref<string>('')
onMounted(async () => {
const res = await getAliappConfig()

View File

@ -1,12 +1,12 @@
<template>
<!--配置教程-->
<div class="main-container">
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<div class="flex">
<div class="min-w-[60px]">
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
@ -51,8 +51,8 @@
</div>
</div>
</div>
</el-card>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>

View File

@ -24,11 +24,7 @@
</div>
<div class="py-[20px] px-[30px] h-[350px]">
<div v-if="formData.msgtype == 'text'">
<el-input
v-model.trim="formData.text.content" :rows="5" type="textarea" placeholder="" maxlength="600" :show-word-limit="true"
resize="none"
input-style="box-shadow: none;height:300px"
/>
<el-input v-model.trim="formData.text.content" :rows="5" type="textarea" placeholder="" maxlength="600" :show-word-limit="true" resize="none" input-style="box-shadow: none;height:300px" />
</div>
<div v-if="formData.msgtype == 'image'" class="flex w-full h-full justify-center items-center image-media">
<div class="w-full h-full" v-if="formData.image.url">

View File

@ -14,7 +14,9 @@
</upload-media>
</div>
<div class="flex" v-else>
<el-button type="primary" :loading="syncLoading" @click="syncWechatNews">{{ syncLoading ? '同步中' : '同步微信图文'}}</el-button>
<el-button type="primary" :loading="syncLoading" @click="syncWechatNews">
{{ syncLoading ? '同步中' : '同步微信图文' }}
</el-button>
</div>
</el-col>
</el-row>
@ -23,8 +25,10 @@
<!-- 素材管理 -->
<div v-if="attachment.data.length">
<div class="flex flex-wrap" v-if="prop.type != 'news'">
<div class="attachment-item mr-[10px] mb-[10px] w-[120px]" v-for="(item, index) in attachment.data" :key="index" @click="selectedFile = item">
<div class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
<div class="attachment-item mr-[10px] mb-[10px] w-[120px]"
v-for="(item, index) in attachment.data" :key="index" @click="selectedFile = item">
<div
class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
<el-image :src="img(item.value)" fit="contain" v-if="type == 'image'" :preview-src-list="item.image_list" />
<video :src="img(item.value)" v-else-if="type == 'video'"></video>
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
@ -34,12 +38,14 @@
</div>
</div>
<div class="relative" ref="waterfallContainerRef" v-else>
<div ref="waterfallItemRef" class="absolute attachment-item mr-[10px] mb-[10px] w-[280px] rounded-lg overflow-hidden border border-color" v-for="(item, index) in attachment.data"
<div ref="waterfallItemRef"
class="absolute attachment-item mr-[10px] mb-[10px] w-[280px] rounded-lg overflow-hidden border border-color"
v-for="(item, index) in attachment.data"
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }"
:key="index" @click="selectedFile = item">
<div class="relative">
<div class="w-full h-[130px] relative">
<el-image :src="item.value.news_item[0].thumb_url" class="w-full h-full"/>
<el-image :src="item.value.news_item[0].thumb_url" class="w-full h-full" />
<div class="absolute left-0 bottom-0 p-[10px] w-full truncate text-white leading-none" v-if="item.value.news_item.length > 1">
{{ item.value.news_item[0].title }}
</div>
@ -47,18 +53,14 @@
<div v-if="item.value.news_item.length > 1">
<template v-for="(newsItem, newsIndex) in item.value.news_item">
<div class="px-[15px] py-[10px] flex" :class="{'border-b border-color' : newsIndex < item.value.news_item.length - 1 }" v-if="newsIndex > 0">
<div class="flex-1 w-0 truncate">
{{ newsItem.title }}
</div>
<div class="flex-1 w-0 truncate">{{ newsItem.title }}</div>
<div class="w-[50px] h-[50px] ml-[10px]">
<el-image :src="newsItem.thumb_url" class="w-full h-full"/>
<el-image :src="newsItem.thumb_url" class="w-full h-full" />
</div>
</div>
</template>
</div>
<div class="px-[15px] py-[10px]" v-else>
{{ item.value.news_item[0].title }}
</div>
<div class="px-[15px] py-[10px]" v-else>{{ item.value.news_item[0].title }}</div>
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
<icon name="element Select" color="#fff" size="40px" />
</div>
@ -75,9 +77,9 @@
<el-col span="24">
<div class="flex h-full justify-end items-center">
<el-pagination v-model:current-page="attachment.page" :small="true"
v-model:page-size="attachment.limit" :page-sizes="[10, 20, 30, 40, 60]"
layout="total, sizes, prev, pager, next, jumper" :total="attachment.total"
@size-change="getAttachmentList()" @current-change="getAttachmentList" />
v-model:page-size="attachment.limit" :page-sizes="[10, 20, 30, 40, 60]"
layout="total, sizes, prev, pager, next, jumper" :total="attachment.total"
@size-change="getAttachmentList()" @current-change="getAttachmentList" />
</div>
</el-col>
</el-row>
@ -94,118 +96,118 @@
</template>
<script lang="ts" setup>
import { reactive, ref, nextTick } from 'vue'
import { t } from '@/lang'
import UploadMedia from './upload-media.vue'
import { img, debounce } from '@/utils/common'
import { getMediaList, syncNews } from '@/app/api/wechat'
import { reactive, ref, nextTick } from 'vue'
import { t } from '@/lang'
import UploadMedia from './upload-media.vue'
import { img, debounce } from '@/utils/common'
import { getMediaList, syncNews } from '@/app/api/wechat'
const prop = defineProps({
type: {
type: String,
default: 'image'
}
})
const prop = defineProps({
type: {
type: String,
default: 'image'
}
})
const showDialog = ref(false)
const showDialog = ref(false)
const openDialog = () => {
const openDialog = () => {
prop.type == 'news' && waterfall()
showDialog.value = true
}
const attachment: Record<string, any> = reactive({
loading: true,
page: 1,
total: 0,
limit: 10,
data: []
})
/**
* 查询素材
*/
const getAttachmentList = (page: number = 1) => {
attachment.loading = true
attachment.page = page
getMediaList({
page: attachment.page,
limit: attachment.limit,
type: prop.type
}).then(res => {
attachment.data = res.data.data
attachment.total = res.data.total
attachment.loading = false
prop.type == 'news' && waterfall()
showDialog.value = true
}
const attachment: Record<string, any> = reactive({
loading: true,
page: 1,
total: 0,
limit: 10,
data: []
}).catch(() => {
attachment.loading = false
})
}
getAttachmentList()
/**
* 查询素材
*/
const getAttachmentList = (page: number = 1) => {
attachment.loading = true
attachment.page = page
getMediaList({
page: attachment.page,
limit: attachment.limit,
type: prop.type
}).then(res => {
attachment.data = res.data.data
attachment.total = res.data.total
attachment.loading = false
prop.type == 'news' && waterfall()
}).catch(() => {
attachment.loading = false
})
}
getAttachmentList()
const emits = defineEmits(['success'])
const selectedFile: Record<string, any> = ref({})
const emits = defineEmits(['success'])
const selectedFile: Record<string, any> = ref({})
const confirm = () => {
emits('success', selectedFile.value)
}
const confirm = () => {
emits('success', selectedFile.value)
}
const syncLoading = ref(false)
const syncWechatNews = () => {
if (syncLoading.value) return
syncLoading.value = true
const syncLoading = ref(false)
const syncWechatNews = () => {
if (syncLoading.value) return
syncLoading.value = true
syncNews().then(() => {
syncLoading.value = false
getAttachmentList()
}).catch(() => {
syncLoading.value = false
})
}
syncNews().then(() => {
syncLoading.value = false
getAttachmentList()
}).catch(() => {
syncLoading.value = false
})
}
const meta = document.createElement('meta')
meta.content = 'same-origin'
meta.name = 'referrer'
document.getElementsByTagName('head')[0].appendChild(meta)
const meta = document.createElement('meta')
meta.content = 'same-origin'
meta.name = 'referrer'
document.getElementsByTagName('head')[0].appendChild(meta)
//
const waterfallContainerRef = ref(null)
const waterfallItemRef = ref([])
const listPosition = ref([])
const waterfall = debounce(() => {
nextTick(() => {
const containerWidth = waterfallContainerRef.value.clientWidth
const column = parseInt(containerWidth / 292)
const heights = []
const positions = []
//
const waterfallContainerRef = ref(null)
const waterfallItemRef = ref([])
const listPosition = ref([])
const waterfall = debounce(() => {
nextTick(() => {
const containerWidth = waterfallContainerRef.value.clientWidth
const column = parseInt(containerWidth / 292)
const heights = []
const positions = []
waterfallItemRef.value.forEach((item, i) => {
if (i < column) {
const position = {}
position.top = '0px'
if (i % column == 0) {
position.left = item.clientWidth * i + "px"
} else {
position.left = item.clientWidth * i + (i % column * 10) + "px"
}
positions[i] = position
heights[i] = item.clientHeight + 10
waterfallItemRef.value.forEach((item, i) => {
if (i < column) {
const position = {}
position.top = '0px'
if (i % column == 0) {
position.left = item.clientWidth * i + "px"
} else {
let minHeight = Math.min(...heights) //
let minIndex = heights.findIndex(item => item === minHeight) //
let position = {}
position.top = minHeight + 10 + "px"
position.left = positions[minIndex].left
positions[i] = position
heights[minIndex] += item.clientHeight + 10
position.left = item.clientWidth * i + (i % column * 10) + "px"
}
})
listPosition.value = positions
positions[i] = position
heights[i] = item.clientHeight + 10
} else {
let minHeight = Math.min(...heights) //
let minIndex = heights.findIndex(item => item === minHeight) //
let position = {}
position.top = minHeight + 10 + "px"
position.left = positions[minIndex].left
positions[i] = position
heights[minIndex] += item.clientHeight + 10
}
})
}, 800)
listPosition.value = positions
})
}, 800)
//
window.addEventListener('resize', () => waterfall())
//
window.addEventListener('resize', () => waterfall())
</script>
<style lang="scss" scoped>

View File

@ -83,8 +83,7 @@
<el-form-item :label="t('businessDomain')">
<el-input :model-value="wechatStatic.business_domain" placeholder="Please input" class="input-width" :readonly="true">
<template #append>
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
</div>
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}</div>
</template>
</el-input>
</el-form-item>
@ -92,8 +91,7 @@
<el-form-item :label="t('jsSecureDomain')">
<el-input :model-value="wechatStatic.js_secure_domain" placeholder="Please input" class="input-width" :readonly="true">
<template #append>
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
</div>
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}</div>
</template>
</el-input>
</el-form-item>
@ -101,8 +99,7 @@
<el-form-item :label="t('webAuthDomain')">
<el-input :model-value="wechatStatic.web_auth_domain" placeholder="Please input" class="input-width" :readonly="true">
<template #append>
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}
</div>
<div class="cursor-pointer" @click="copyEvent(wechatStatic.business_domain)">{{ t('copy') }}</div>
</template>
</el-input>
</el-form-item>

View File

@ -12,9 +12,7 @@
<span class="flex justify-center items-center block w-[40px] h-[40px] border-[1px] border-primary rounded-[999px] text-primary">1</span>
</div>
<div>
<p class="flex items-center text-[14px]">{{ t('writingTipsOne1') }}--<el-button link type="primary"
@click="linkEvent">{{ t('writingTipsOne2') }}</el-button>, {{ t('writingTipsOne3') }}<span
class="text-primary">URL / Token / EncondingAESKey</span>{{ t('writingTipsOne4') }}</p>
<p class="flex items-center text-[14px]">{{ t('writingTipsOne1') }}--<el-button link type="primary" @click="linkEvent">{{ t('writingTipsOne2') }}</el-button>, {{ t('writingTipsOne3') }}<span class="text-primary">URL / Token / EncondingAESKey</span>{{ t('writingTipsOne4') }}</p>
<div class="w-[100%] mt-[10px]">
<img class="w-[100%]" src="@/app/assets/images/setting/wechat_1.png" />
</div>

View File

@ -83,7 +83,7 @@
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getKeywordsReplyInfo, editKeywordsReply, addKeywordsReply } from '@/app/api/wechat'
import { ElMessage, FormInstance, FormRules } from 'element-plus'
import { FormInstance, FormRules } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { useRoute, useRouter } from 'vue-router'
import ReplyForm from '@/app/views/channel/wechat/components/reply-form.vue'

View File

@ -110,60 +110,6 @@ const setFormData = async (row: any = null) => {
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
if (value && !/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value)) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
// 1
const minInputVerify = (rule: any, value: any, callback: any) => {
if (value && !/^\d{0,}$/.test(value)) {
callback(new Error(t('generateMin')))
} else {
callback()
}
}
// 150
const maxInputVerify = (rule: any, value: any, callback: any) => {
if (value && !/^\d{0,150}$/.test(value)) {
callback(new Error(t('generateMax')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData

View File

@ -27,10 +27,9 @@
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { FormRules } from 'element-plus'
import { cloneDeep } from 'lodash-es'
import useDiyStore from '@/stores/modules/diy'
const diyStore = useDiyStore()
@ -70,37 +69,50 @@ const open = (option:any) => {
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({
title: [
{ required: true, message: "请输入颜色名称", trigger: 'blur' }
],
value: [
{ required: true, message: "请输入颜色value值", trigger: 'blur' }
],
label: [
{ required: true, message: "请输入颜色key值", trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
const regex = /^[a-zA-Z0-9-]+$/
if (keyArr.indexOf(value) != -1) {
callback('新增颜色key值与已存在颜色key值命名重复请修改命名')
} if (!regex.test(value)) {
callback('颜色key值只能输入字母、数字和连字符')
} else{
callback();
}
},
trigger: 'blur'
}
]
const formRules = computed(() => {
return {
title: [
{ required: true, message: "请输入颜色名称", trigger: 'blur' }
],
value: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback('请输入颜色value值')
} else{
callback();
}
},
trigger: ['blur', 'change']
}
],
label: [
{ required: true, message: "请输入颜色key值", trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
const regex = /^[a-zA-Z0-9-]+$/
if (keyArr.indexOf(value) != -1) {
callback('新增颜色key值与已存在颜色key值命名重复请修改命名')
} if (!regex.test(value)) {
callback('颜色key值只能输入字母、数字和连字符')
} else{
callback();
}
},
trigger: 'blur'
}
]
}
})
const confirmFn = async (formEl: FormInstance | undefined) => {
if (confirmRepeat.value || !formEl) return
await formEl.validate(async (valid) => {
if (confirmRepeat.value) return
await formRef.value?.validate(async (valid) => {
if (confirmRepeat.value) return
confirmRepeat.value = true
if (valid) {
confirmRepeat.value = false
emit('confirm', cloneDeep(formData));
dialogThemeVisible.value = false;
}

View File

@ -1,102 +1,109 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('titleContent') }}</h3>
<el-form label-width="80px" class="px-[10px]" @submit.prevent>
<el-form-item :label="t('selectStyle')" class="flex">
<span class="text-primary flex-1 cursor-pointer" @click="showTitleStyle">{{ diyStore.editComponent.titleStyle.title }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
<el-form-item :label="t('title')" v-if="diyStore.editComponent && diyStore.editComponent.titleStyle && diyStore.editComponent.titleStyle.value != 'style-5'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('titleContent') }}</h3>
<el-form label-width="80px" class="px-[10px]" @submit.prevent>
<el-form-item :label="t('selectStyle')" class="flex">
<span class="text-primary flex-1 cursor-pointer" @click="showTitleStyle">{{ diyStore.editComponent.titleStyle.title }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
<el-form-item :label="t('title')" v-if="diyStore.editComponent && diyStore.editComponent.titleStyle && diyStore.editComponent.titleStyle.value != 'style-5'">
<el-input v-model.trim="diyStore.editComponent.text" :placeholder="t('titlePlaceholder')" clearable maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('image')" v-else>
<upload-image v-model="diyStore.editComponent.textImg" :limit="1"/>
<upload-image v-model="diyStore.editComponent.textImg" :limit="1" />
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="diyStore.editComponent.textLink"/>
<diy-link v-model="diyStore.editComponent.textLink" />
</el-form-item>
<el-form-item :label="t('subTitle')">
<el-input v-model.trim="diyStore.editComponent.subTitle.text" :placeholder="t('subTitlePlaceholder')" clearable maxlength="8" show-word-limit />
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="diyStore.editComponent.subTitle.link"/>
<diy-link v-model="diyStore.editComponent.subTitle.link" />
</el-form-item>
</el-form>
</el-form>
<el-dialog v-model="showTitleDialog" :title="t('selectStyle')" width="460px">
<el-dialog v-model="showTitleDialog" :title="t('selectStyle')" width="460px">
<div class="flex flex-wrap">
<template v-for="(item,index) in titleStyleList" :key="index">
<div :class="{ 'border-primary': selectTitleStyle.value == item.value }" @click="changeTitleStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
</div>
<div class="flex flex-wrap">
<template v-for="(item,index) in titleStyleList" :key="index">
<div :class="{ 'border-primary': selectTitleStyle.value == item.value }"
@click="changeTitleStyle(item)"
class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showTitleDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmTitleStyle">{{ t('confirm') }}</el-button>
</span>
</template>
<template #footer>
<span class="dialog-footer">
<el-button @click="showTitleDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmTitleStyle">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('activeCubeBlockContent') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('activeCubeBlockContent') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('selectStyle')" class="flex">
<span class="text-primary flex-1 cursor-pointer" @click="showBlockStyle">{{ diyStore.editComponent.blockStyle.title }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
<el-form-item :label="t('selectStyle')" class="flex">
<span class="text-primary flex-1 cursor-pointer"
@click="showBlockStyle">{{ diyStore.editComponent.blockStyle.title }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
<el-dialog v-model="showListDialog" :title="t('selectStyle')" width="600px">
<div class="flex flex-wrap">
<template v-for="(item,index) in blockStyleList" :key="index">
<div :class="{ 'border-primary': selectBlockStyle.value == item.value }" @click="changeBlockStyle(item)" class="flex items-center justify-center overflow-hidden w-[250px] h-[150px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
<el-dialog v-model="showListDialog" :title="t('selectStyle')" width="600px">
<div class="flex flex-wrap">
<template v-for="(item,index) in blockStyleList" :key="index">
<div :class="{ 'border-primary': selectBlockStyle.value == item.value }"
@click="changeBlockStyle(item)"
class="flex items-center justify-center overflow-hidden w-[250px] h-[150px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showListDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmBlockStyle">{{ t('confirm') }}</el-button>
</span>
</template>
<template #footer>
<span class="dialog-footer">
<el-button @click="showListDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmBlockStyle">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</el-dialog>
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
<div ref="blockBoxRef">
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('image')">
<upload-image v-model="item.imageUrl" :limit="1"/>
</el-form-item>
<div ref="blockBoxRef">
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id"
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('image')">
<upload-image v-model="item.imageUrl" :limit="1" />
</el-form-item>
<el-form-item :label="t('activeCubeTitle')">
<el-input v-model.trim="item.title.text" :placeholder="t('activeCubeTitlePlaceholder')" clearable maxlength="4" show-word-limit/>
</el-form-item>
<el-form-item :label="t('activeCubeTitle')">
<el-input v-model.trim="item.title.text" :placeholder="t('activeCubeTitlePlaceholder')"
clearable maxlength="4" show-word-limit />
</el-form-item>
<el-form-item :label="t('activeCubeSubTitleTextColor')" v-show="diyStore.editComponent.blockStyle.value == 'style-3'">
<el-color-picker v-model="item.title.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('activeCubeSubTitle')" v-if="diyStore.editComponent.blockStyle.value != 'style-3'">
<el-input v-model.trim="item.subTitle.text" :placeholder="t('activeCubeSubTitlePlaceholder')" clearable maxlength="6" show-word-limit/>
</el-form-item>
<el-form-item :label="t('activeCubeSubTitle')" v-if="diyStore.editComponent.blockStyle.value != 'style-3'">
<el-input v-model.trim="item.subTitle.text" :placeholder="t('activeCubeSubTitlePlaceholder')" clearable maxlength="6" show-word-limit />
</el-form-item>
<div v-show="diyStore.editComponent.blockStyle.value == 'style-4'">
<el-form-item :label="t('activeCubeSubTitleTextColor')">
@ -104,99 +111,105 @@
</el-form-item>
<el-form-item :label="t('activeCubeSubTitleBgColor')">
<el-color-picker v-model="item.subTitle.startColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="item.subTitle.endColor" show-alpha :predefine="diyStore.predefineColors"/>
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="item.subTitle.endColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</div>
<el-form-item :label="t('activeListFrameColor')">
<el-color-picker v-model="item.listFrame.startColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="item.listFrame.endColor" show-alpha :predefine="diyStore.predefineColors"/>
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="item.listFrame.endColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<div v-show="diyStore.editComponent.blockStyle.value != 'style-4' && diyStore.editComponent.blockStyle.value != 'style-3'">
<div
v-show="diyStore.editComponent.blockStyle.value != 'style-4' && diyStore.editComponent.blockStyle.value != 'style-3'">
<el-form-item :label="t('activeCubeButton')">
<el-input v-model.trim="item.moreTitle.text" :placeholder="t('activeCubeButtonPlaceholder')" clearable maxlength="3" show-word-limit/>
<el-input v-model.trim="item.moreTitle.text" :placeholder="t('activeCubeButtonPlaceholder')" clearable maxlength="3" show-word-limit />
</el-form-item>
<el-form-item :label="t('activeCubeButtonColor')">
<el-color-picker v-model="item.moreTitle.startColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="item.moreTitle.endColor" show-alpha :predefine="diyStore.predefineColors"/>
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="item.moreTitle.endColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</div>
<el-form-item :label="t('link')">
<diy-link v-model="item.link"/>
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="item.link" />
</el-form-item>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.list.length > 1" @click="diyStore.editComponent.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px"/>
</div>
</div>
</div>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.list.length > 1"
@click="diyStore.editComponent.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
</div>
</div>
</div>
<el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addItem">{{ t('activeCubeAddItem') }}</el-button>
<el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addItem">
{{ t('activeCubeAddItem') }}
</el-button>
</el-form>
</div>
</el-form>
</div>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap" v-if="selectTitleStyle.value != 'style-5'">
<h3 class="mb-[10px]">{{ t('titleStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('textColor')">
<el-color-picker v-model="diyStore.editComponent.titleColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap" v-if="selectTitleStyle.value != 'style-5'">
<h3 class="mb-[10px]">{{ t('titleStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('textColor')">
<el-color-picker v-model="diyStore.editComponent.titleColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('subTitleStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('textColor')">
<el-color-picker v-model="diyStore.editComponent.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('subTextBgColor')">
<el-color-picker v-model="diyStore.editComponent.subTitle.startColor" show-alpha :predefine="diyStore.predefineColors"/>
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.subTitle.endColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('subTitleStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('textColor')">
<el-color-picker v-model="diyStore.editComponent.subTitle.textColor" show-alpha
:predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('subTextBgColor')">
<el-color-picker v-model="diyStore.editComponent.subTitle.startColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="diyStore.editComponent.subTitle.endColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('activeCubeBlockStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('activeCubeBlockTextFontWeight')">
<el-radio-group v-model="diyStore.editComponent.blockStyle.fontWeight">
<el-radio :label="'normal'">{{t('fontWeightNormal')}}</el-radio>
<el-radio :label="'bold'">{{t('fontWeightBold')}}</el-radio>
</el-radio-group>
</el-form-item>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('activeCubeBlockStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('activeCubeBlockTextFontWeight')">
<el-radio-group v-model="diyStore.editComponent.blockStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('activeCubeBlockBtnText')" class="flex">
<el-radio-group v-model="diyStore.editComponent.blockStyle.btnText">
<el-radio :label="'normal'">{{t('btnTextNormal')}}</el-radio>
<el-radio :label="'italics'">{{t('btnTextItalics')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<el-radio-group v-model="diyStore.editComponent.blockStyle.btnText">
<el-radio :label="'normal'">{{ t('btnTextNormal') }}</el-radio>
<el-radio :label="'italics'">{{ t('btnTextItalics') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
@ -215,7 +228,7 @@ diyStore.editComponent.ignore = [] // 忽略公共属性
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
if(diyStore.value[index].text == ''){
if (diyStore.value[index].text == '') {
res.code = false
res.message = t('activeCubeTitlePlaceholder')
return res
@ -232,14 +245,14 @@ diyStore.editComponent.verify = (index: number) => {
res.message = t('activeCubeTitlePlaceholder')
return res
}
if(['style-1','style-2','style-4'].indexOf(diyStore.value[index].blockStyle.value) != -1){
if (['style-1', 'style-2', 'style-4'].indexOf(diyStore.value[index].blockStyle.value) != -1) {
if (item.subTitle.text === '') {
res.code = false
res.message = t('activeCubeSubTitlePlaceholder')
return res
}
}
if(['style-1','style-2'].indexOf(diyStore.value[index].blockStyle.value) != -1){
if (['style-1', 'style-2'].indexOf(diyStore.value[index].blockStyle.value) != -1) {
if (item.moreTitle.text === '') {
res.code = false
res.message = t('activeCubeButtonPlaceholder')
@ -258,33 +271,33 @@ diyStore.editComponent.list.forEach((item: any) => {
const showTitleDialog = ref(false)
const showTitleStyle = () => {
selectTitleStyle.title = diyStore.editComponent.titleStyle.title;
selectTitleStyle.value = diyStore.editComponent.titleStyle.value;
selectTitleStyle.title = diyStore.editComponent.titleStyle.title;
selectTitleStyle.value = diyStore.editComponent.titleStyle.value;
showTitleDialog.value = true
}
const titleStyleList = reactive([
{
url : 'static/resource/images/diy/active_cube/title_style1.png',
title:'风格1',
value:'style-1'
},{
url : 'static/resource/images/diy/active_cube/title_style2.png',
title:'风格2',
value:'style-2'
},{
url : 'static/resource/images/diy/active_cube/title_style3.png',
title:'风格3',
value:'style-3'
},{
url : 'static/resource/images/diy/active_cube/title_style5.png',
title:'风格4',
value:'style-4'
},{
url : 'static/resource/images/diy/active_cube/title_style6.png',
title:'风格5',
value:'style-5'
}
{
url: 'static/resource/images/diy/active_cube/title_style1.png',
title: '风格1',
value: 'style-1'
}, {
url: 'static/resource/images/diy/active_cube/title_style2.png',
title: '风格2',
value: 'style-2'
}, {
url: 'static/resource/images/diy/active_cube/title_style3.png',
title: '风格3',
value: 'style-3'
}, {
url: 'static/resource/images/diy/active_cube/title_style5.png',
title: '风格4',
value: 'style-4'
}, {
url: 'static/resource/images/diy/active_cube/title_style6.png',
title: '风格5',
value: 'style-5'
}
])
const selectTitleStyle = reactive({
@ -292,7 +305,7 @@ const selectTitleStyle = reactive({
value: diyStore.editComponent.titleStyle.value
})
const changeTitleStyle = (item:any) => {
const changeTitleStyle = (item: any) => {
selectTitleStyle.title = item.title;
selectTitleStyle.value = item.value;
}
@ -304,28 +317,28 @@ const confirmTitleStyle = () => {
showTitleDialog.value = false
}
const initTitleStyle = (style)=>{
if(diyStore.editComponent.titleStyle.value == 'style-1'){
const initTitleStyle = (style) => {
if (diyStore.editComponent.titleStyle.value == 'style-1') {
diyStore.editComponent.titleColor = "#F91700";
diyStore.editComponent.subTitle.textColor = "#FFFFFF";
diyStore.editComponent.subTitle.startColor = "#FB792F";
diyStore.editComponent.subTitle.endColor = "#F91700";
}else if(diyStore.editComponent.titleStyle.value == 'style-2'){
} else if (diyStore.editComponent.titleStyle.value == 'style-2') {
diyStore.editComponent.titleColor = "#F91700";
diyStore.editComponent.subTitle.textColor = "#FFFFFF";
diyStore.editComponent.subTitle.startColor = "#FB792F";
diyStore.editComponent.subTitle.endColor = "#F91700";
}else if(diyStore.editComponent.titleStyle.value == 'style-3'){
} else if (diyStore.editComponent.titleStyle.value == 'style-3') {
diyStore.editComponent.titleColor = "#F91700";
diyStore.editComponent.subTitle.textColor = "#FFFFFF";
diyStore.editComponent.subTitle.startColor = "#FB792F";
diyStore.editComponent.subTitle.endColor = "#F91700";
}else if(diyStore.editComponent.titleStyle.value == 'style-4'){
} else if (diyStore.editComponent.titleStyle.value == 'style-4') {
diyStore.editComponent.titleColor = "#FFFFFF";
diyStore.editComponent.subTitle.textColor = "#333333";
diyStore.editComponent.subTitle.startColor = "#FFFFFF";
diyStore.editComponent.subTitle.endColor = "#FFFFFF";
}else if(diyStore.editComponent.titleStyle.value == 'style-5'){
} else if (diyStore.editComponent.titleStyle.value == 'style-5') {
diyStore.editComponent.titleColor = "";
diyStore.editComponent.subTitle.textColor = "#999999";
diyStore.editComponent.subTitle.startColor = "#FFFFFF";
@ -342,24 +355,24 @@ const showBlockStyle = () => {
const blockStyleList = reactive([
{
url : 'static/resource/images/diy/active_cube/block_style1.png',
title:'风格1',
value:'style-1'
url: 'static/resource/images/diy/active_cube/block_style1.png',
title: '风格1',
value: 'style-1'
},
{
url : 'static/resource/images/diy/active_cube/block_style2.png',
title:'风格2',
value:'style-2'
url: 'static/resource/images/diy/active_cube/block_style2.png',
title: '风格2',
value: 'style-2'
},
{
url : 'static/resource/images/diy/active_cube/block_style3.png',
title:'风格3',
value:'style-3'
url: 'static/resource/images/diy/active_cube/block_style3.png',
title: '风格3',
value: 'style-3'
},
{
url : 'static/resource/images/diy/active_cube/block_style4.png',
title:'风格4',
value:'style-4'
url: 'static/resource/images/diy/active_cube/block_style4.png',
title: '风格4',
value: 'style-4'
}
])
@ -368,7 +381,7 @@ const selectBlockStyle = reactive({
value: diyStore.editComponent.blockStyle.value
})
const changeBlockStyle = (item:any) => {
const changeBlockStyle = (item: any) => {
selectBlockStyle.title = item.title;
selectBlockStyle.value = item.value;
}
@ -380,8 +393,8 @@ const confirmBlockStyle = () => {
showListDialog.value = false
}
const initBlockStyle = (style: any)=>{
if(style == 'style-1'){
const initBlockStyle = (style: any) => {
if (style == 'style-1') {
diyStore.editComponent.blockStyle.fontWeight = "normal";
diyStore.editComponent.blockStyle.btnText = "normal";
@ -421,7 +434,7 @@ const initBlockStyle = (style: any)=>{
diyStore.editComponent.list[3].listFrame.startColor = "#FFFAF5";
diyStore.editComponent.list[3].listFrame.endColor = "#FFFFFF";
}else if(style == 'style-2'){
} else if (style == 'style-2') {
diyStore.editComponent.blockStyle.fontWeight = "normal";
diyStore.editComponent.blockStyle.btnText = "normal";
@ -463,7 +476,7 @@ const initBlockStyle = (style: any)=>{
diyStore.editComponent.list[3].moreTitle.endColor = "#F91700";
diyStore.editComponent.list[3].listFrame.startColor = "#FFEAEA";
diyStore.editComponent.list[3].listFrame.endColor = "#FFFCFB";
}else if(style == 'style-3'){
} else if (style == 'style-3') {
diyStore.editComponent.blockStyle.fontWeight = "normal";
diyStore.editComponent.blockStyle.btnText = "normal";
@ -502,7 +515,7 @@ const initBlockStyle = (style: any)=>{
diyStore.editComponent.list[3].moreTitle.endColor = "";
diyStore.editComponent.list[3].listFrame.startColor = "";
diyStore.editComponent.list[3].listFrame.endColor = "";
}else if(style == 'style-4'){
} else if (style == 'style-4') {
diyStore.editComponent.blockStyle.fontWeight = "bold";
diyStore.editComponent.blockStyle.btnText = "normal";
@ -547,24 +560,24 @@ const initBlockStyle = (style: any)=>{
const addItem = () => {
diyStore.editComponent.list.push({
id: diyStore.generateRandom(),
title:{
title:'标题',
title: {
title: '标题',
textColor: "#000000"
},
subTitle:{
text:'副标题',
subTitle: {
text: '副标题',
textColor: "#999999",
startColor:'',
endColor:''
startColor: '',
endColor: ''
},
listFrame: {
startColor: "#4AC1FF",
endColor: "#1D7CFF"
},
moreTitle:{
text:'去看看',
startColor:'#FEA715',
endColor:'#FE1E00'
moreTitle: {
text: '去看看',
startColor: '#FEA715',
endColor: '#FE1E00'
},
imageUrl: '',
link: { name: '' }

View File

@ -1,105 +1,106 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchShowPosition') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchShowWay')">
<el-radio-group v-model="diyStore.editComponent.positionWay">
<el-radio label="static">{{ t('carouselSearchShowWayStatic') }}</el-radio>
<el-radio label="fixed">{{ t('carouselSearchShowWayFixed') }}</el-radio>
</el-radio-group>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchShowPosition') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchShowWay')">
<el-radio-group v-model="diyStore.editComponent.positionWay">
<el-radio label="static">{{ t('carouselSearchShowWayStatic') }}</el-radio>
<el-radio label="fixed">{{ t('carouselSearchShowWayFixed') }}</el-radio>
</el-radio-group>
<div v-if="diyStore.editComponent.positionWay == 'fixed'" class="text-sm text-gray-400 mb-[10px]">滑动页面查看效果</div>
</el-form-item>
<el-form-item :label="t('carouselSearchFixedBgColor')" v-show="diyStore.editComponent.positionWay == 'fixed'">
<el-color-picker v-model="diyStore.editComponent.fixedBgColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form-item>
<el-form-item :label="t('carouselSearchFixedBgColor')" v-show="diyStore.editComponent.positionWay == 'fixed'">
<el-color-picker v-model="diyStore.editComponent.fixedBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('carouselSearchBgGradient')">
<el-radio-group v-model="diyStore.editComponent.bgGradient">
<el-radio :label="true">{{ t('carouselSearchOpen') }}</el-radio>
<el-radio :label="false">{{ t('carouselSearchClose') }}</el-radio>
</el-radio-group>
<el-radio :label="true">{{ t('carouselSearchOpen') }}</el-radio>
<el-radio :label="false">{{ t('carouselSearchClose') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchSet') }}</h3>
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
<el-form-item :label="t('selectStyle')" class="flex">
<span class="text-primary flex-1 cursor-pointer" @click="showSearchStyle">{{ diyStore.editComponent.search.styleName }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchSet') }}</h3>
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
<el-form-item :label="t('selectStyle')" class="flex">
<span class="text-primary flex-1 cursor-pointer" @click="showSearchStyle">{{ diyStore.editComponent.search.styleName }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
<el-form-item :label="t('carouselSearchSubTitle')" v-if="diyStore.editComponent.search.style == 'style-2'">
<el-input v-model.trim="diyStore.editComponent.search.subTitle.text" :placeholder="t('carouselSearchSubTitlePlaceholder')" clearable maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('logo')">
<upload-image v-model="diyStore.editComponent.search.logo" :limit="1" />
<div class="text-sm text-gray-400 mb-[10px]">{{ t('carouselSearchLogoTips') }}</div>
</el-form-item>
<el-form-item :label="t('carouselSearchText')">
<div>
</el-form-item>
<el-form-item :label="t('logo')">
<upload-image v-model="diyStore.editComponent.search.logo" :limit="1" />
<div class="text-sm text-gray-400 mb-[10px]">{{ t('carouselSearchLogoTips') }}</div>
</el-form-item>
<el-form-item :label="t('carouselSearchText')">
<div>
<el-input v-model.trim="diyStore.editComponent.search.text" :placeholder="t('carouselSearchPlaceholder')" clearable maxlength="20" show-word-limit />
<p class="text-sm text-gray-400 mt-[10px] leading-[1.5]">{{t('carouselSearchTextTips')}}</p>
<p class="text-sm text-gray-400 mt-[10px] leading-[1.5]">{{ t('carouselSearchTextTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="diyStore.editComponent.search.link"/>
</el-form-item>
</el-form>
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="diyStore.editComponent.search.link" />
</el-form-item>
</el-form>
<el-dialog v-model="showSearchDialog" :title="t('selectStyle')" width="500px">
<div class="flex flex-wrap">
<template v-for="(item,index) in searchStyleList" :key="index">
<div :class="{ 'border-primary': selectSearchStyle.value == item.value }" @click="changeSearchStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] m-[6px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
</div>
<div class="flex flex-wrap">
<template v-for="(item,index) in searchStyleList" :key="index">
<div :class="{ 'border-primary': selectSearchStyle.value == item.value }" @click="changeSearchStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] m-[6px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showSearchDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmSearchStyle">{{ t('confirm') }}</el-button>
</span>
</template>
<template #footer>
<span class="dialog-footer">
<el-button @click="showSearchDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmSearchStyle">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</el-dialog>
</div>
<div class="edit-attr-item-wrap mb-[20px]">
<h3 class="mb-[10px]">{{ t('carouselSearchHotWordSet') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<div class="edit-attr-item-wrap mb-[20px]">
<h3 class="mb-[10px]">{{ t('carouselSearchHotWordSet') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchHotWordInterval')">
<el-slider v-model="diyStore.editComponent.search.hotWord.interval" show-input size="small" class="ml-[10px] diy-nav-slider" :min="1" :max="10"/>
</el-form-item>
<el-form-item :label="t('carouselSearchHotWordInterval')">
<el-slider v-model="diyStore.editComponent.search.hotWord.interval" show-input size="small" class="ml-[10px] diy-nav-slider" :min="1" :max="10" />
</el-form-item>
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
<div ref="searchHotWordTabBoxRef">
<div v-for="(item,index) in diyStore.editComponent.search.hotWord.list" :key="item.id" class="item-wrap p-[10px] relative border border-dashed border-gray-300 mb-[16px]">
<div ref="searchHotWordTabBoxRef">
<div v-for="(item,index) in diyStore.editComponent.search.hotWord.list" :key="item.id"
class="item-wrap p-[10px] relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('carouselSearchHotWordText')" class="!mb-0">
<el-input v-model.trim="item.text" :placeholder="t('carouselSearchHotWordTextPlaceholder')" clearable maxlength="4" show-word-limit/>
</el-form-item>
<el-form-item :label="t('carouselSearchHotWordText')" class="!mb-0">
<el-input v-model.trim="item.text" :placeholder="t('carouselSearchHotWordTextPlaceholder')" clearable maxlength="4" show-word-limit />
</el-form-item>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" @click="diyStore.editComponent.search.hotWord.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px"/>
</div>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" @click="diyStore.editComponent.search.hotWord.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
</div>
</div>
<el-button v-show="diyStore.editComponent.search.hotWord.list.length < 50" class="w-full" @click="addHotWordItem">{{ t('carouselSearchAddHotWordItem') }}</el-button>
</div>
</div>
<el-button v-show="diyStore.editComponent.search.hotWord.list.length < 50" class="w-full" @click="addHotWordItem">{{ t('carouselSearchAddHotWordItem') }}</el-button>
</div>
</el-form>
</div>
</el-form>
</div>
<el-collapse v-model="activeNames" @change="handleChange" class="collapse-wrap">
<el-collapse v-model="activeNames" @change="handleChange" class="collapse-wrap">
<el-collapse-item :title="t('carouselSearchTabSet')" name="tab">
<div class="edit-attr-item-wrap">
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
@ -113,7 +114,7 @@
<div v-for="(item,index) in diyStore.editComponent.tab.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('carouselSearchTabCategoryText')">
<el-input v-model.trim="item.text" :placeholder="t('carouselSearchTabCategoryTextPlaceholder')" clearable maxlength="4" show-word-limit/>
<el-input v-model.trim="item.text" :placeholder="t('carouselSearchTabCategoryTextPlaceholder')" clearable maxlength="4" show-word-limit />
</el-form-item>
<el-form-item :label="t('dataSources')">
@ -131,8 +132,10 @@
</el-input>
</el-form-item>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.tab.list.length > 1" @click="diyStore.editComponent.tab.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px"/>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.tab.list.length > 1"
@click="diyStore.editComponent.tab.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
</div>
</div>
@ -141,7 +144,9 @@
<!-- 选择微页面弹出框 -->
<el-dialog v-model="diyPageShowDialog" :title="t('selectSourcesDiyPage')" width="1000px" :close-on-press-escape="true" :destroy-on-close="true" :close-on-click-modal="false">
<el-table :data="diyPageTable.data" ref="diyPageTableRef" size="large" v-loading="diyPageTable.loading" height="490px" @current-change="handleCurrentDiyPageChange" row-key="id" highlight-current-row>
<el-table :data="diyPageTable.data" ref="diyPageTableRef" size="large"
v-loading="diyPageTable.loading" height="490px"
@current-change="handleCurrentDiyPageChange" row-key="id" highlight-current-row>
<template #empty>
<span>{{ !diyPageTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -150,9 +155,11 @@
<el-table-column prop="type_name" :label="t('diyPageForAddon')" min-width="80" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="diyPageTable.page" v-model:page-size="diyPageTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="diyPageTable.total"
@size-change="loadDiyPageList" @current-change="loadDiyPageList" />
<el-pagination v-model:current-page="diyPageTable.page"
v-model:page-size="diyPageTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="diyPageTable.total"
@size-change="loadDiyPageList" @current-change="loadDiyPageList" />
</div>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveDiyPageId">{{ t('confirm') }}</el-button>
@ -169,144 +176,151 @@
<el-switch v-model="diyStore.editComponent.swiper.control" />
</el-form-item>
<el-form-item :label="t('carouselSearchSwiperInterval')">
<el-slider v-model="diyStore.editComponent.swiper.interval" show-input size="small" class="ml-[10px] diy-nav-slider" :min="1" :max="10"/>
<el-slider v-model="diyStore.editComponent.swiper.interval" show-input size="small" class="ml-[10px] diy-nav-slider" :min="1" :max="10" />
</el-form-item>
<div class="text-sm text-gray-400 mb-[10px]">{{ t('carouselSearchSwiperTips') }}</div>
<div ref="imageBoxRef">
<div v-for="(item,index) in diyStore.editComponent.swiper.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<div v-for="(item,index) in diyStore.editComponent.swiper.list" :key="item.id"
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('image')">
<upload-image v-model="item.imageUrl" :limit="1" @change="selectImg" />
</el-form-item>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.swiper.list.length > 1" @click="diyStore.editComponent.swiper.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px"/>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.swiper.list.length > 1"
@click="diyStore.editComponent.swiper.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
</div>
<el-form-item :label="t('link')">
<diy-link v-model="item.link"/>
<diy-link v-model="item.link" />
</el-form-item>
</div>
</div>
<el-button v-show="diyStore.editComponent.swiper.list.length < 10" class="w-full" @click="addImageAd">{{ t('addImageAd') }}</el-button>
<el-button v-show="diyStore.editComponent.swiper.list.length < 10" class="w-full"
@click="addImageAd">{{ t('addImageAd') }}
</el-button>
</el-form>
</el-collapse-item>
</el-collapse>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.search.style == 'style-2'">
<h3 class="mb-[10px]">{{ t('carouselSearchPositionStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchTextColor')">
<el-color-picker v-model="diyStore.editComponent.search.positionColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
<h3 class="mb-[10px]">{{ t('carouselSearchPositionStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchTextColor')">
<el-color-picker v-model="diyStore.editComponent.search.positionColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.search.style == 'style-2'">
<h3 class="mb-[10px]">{{ t('carouselSearchSubTitleStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchTextColor')">
<el-color-picker v-model="diyStore.editComponent.search.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('carouselSearchBgColor')">
<el-color-picker v-model="diyStore.editComponent.search.subTitle.startColor" :predefine="diyStore.predefineColors" show-alpha/>
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.search.subTitle.endColor" :predefine="diyStore.predefineColors" show-alpha/>
</el-form-item>
</el-form>
</div>
<h3 class="mb-[10px]">{{ t('carouselSearchSubTitleStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchTextColor')">
<el-color-picker v-model="diyStore.editComponent.search.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('carouselSearchBgColor')">
<el-color-picker v-model="diyStore.editComponent.search.subTitle.startColor" :predefine="diyStore.predefineColors" show-alpha />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="diyStore.editComponent.search.subTitle.endColor" :predefine="diyStore.predefineColors" show-alpha />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchTextColor')">
<el-color-picker v-model="diyStore.editComponent.search.color" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('carouselSearchBgColor')">
<el-color-picker v-model="diyStore.editComponent.search.bgColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('carouselSearchBtnColor')">
<el-color-picker v-model="diyStore.editComponent.search.btnColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('carouselSearchBtnBgColor')">
<el-color-picker v-model="diyStore.editComponent.search.btnBgColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
<h3 class="mb-[10px]">{{ t('carouselSearchStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchTextColor')">
<el-color-picker v-model="diyStore.editComponent.search.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('carouselSearchBgColor')">
<el-color-picker v-model="diyStore.editComponent.search.bgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('carouselSearchBtnColor')">
<el-color-picker v-model="diyStore.editComponent.search.btnColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('carouselSearchBtnBgColor')">
<el-color-picker v-model="diyStore.editComponent.search.btnBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchTabStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('noColor')">
<el-color-picker v-model="diyStore.editComponent.tab.noColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('selectColor')">
<el-color-picker v-model="diyStore.editComponent.tab.selectColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('fixedNoColor')">
<el-color-picker v-model="diyStore.editComponent.tab.fixedNoColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('fixedSelectColor')">
<el-color-picker v-model="diyStore.editComponent.tab.fixedSelectColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchTabStyle') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('noColor')">
<el-color-picker v-model="diyStore.editComponent.tab.noColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('selectColor')">
<el-color-picker v-model="diyStore.editComponent.tab.selectColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('fixedNoColor')">
<el-color-picker v-model="diyStore.editComponent.tab.fixedNoColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('fixedSelectColor')">
<el-color-picker v-model="diyStore.editComponent.tab.fixedSelectColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchSwiperSet') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchSwiperSet') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchSwiperStyle')" @change="changeSwiperStyle">
<el-radio-group v-model="diyStore.editComponent.swiper.swiperStyle">
<el-radio label="style-1">{{ t('carouselSearchSwiperIndicatorStyle1') }}</el-radio>
<el-radio label="style-2">{{ t('carouselSearchSwiperIndicatorStyle2') }}</el-radio>
<el-radio label="style-3">{{ t('carouselSearchSwiperIndicatorStyle3') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.swiper.topRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.swiper.bottomRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<el-radio-group v-model="diyStore.editComponent.swiper.swiperStyle">
<el-radio label="style-1">{{ t('carouselSearchSwiperIndicatorStyle1') }}</el-radio>
<el-radio label="style-2">{{ t('carouselSearchSwiperIndicatorStyle2') }}</el-radio>
<el-radio label="style-3">{{ t('carouselSearchSwiperIndicatorStyle3') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.swiper.topRounded" show-input size="small"
class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.swiper.bottomRounded" show-input size="small"
class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchSwiperIndicatorSet') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchSwiperIndicatorStyle')">
<el-radio-group v-model="diyStore.editComponent.swiper.indicatorStyle">
<el-radio label="style-1">{{ t('carouselSearchSwiperIndicatorStyle1') }}</el-radio>
<el-radio label="style-2">{{ t('carouselSearchSwiperIndicatorStyle2') }}</el-radio>
<el-radio label="style-3">{{ t('carouselSearchSwiperIndicatorStyle3') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('carouselSearchSwiperIndicatorAlign')">
<el-radio-group v-model="diyStore.editComponent.swiper.indicatorAlign">
<el-radio label="left">{{ t('alignLeft') }}</el-radio>
<el-radio label="center">{{ t('alignCenter') }}</el-radio>
<el-radio label="right">{{ t('alignRight') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('noColor')">
<el-color-picker v-model="diyStore.editComponent.swiper.indicatorColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('selectColor')">
<el-color-picker v-model="diyStore.editComponent.swiper.indicatorActiveColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('carouselSearchSwiperIndicatorSet') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('carouselSearchSwiperIndicatorStyle')">
<el-radio-group v-model="diyStore.editComponent.swiper.indicatorStyle">
<el-radio label="style-1">{{ t('carouselSearchSwiperIndicatorStyle1') }}</el-radio>
<el-radio label="style-2">{{ t('carouselSearchSwiperIndicatorStyle2') }}</el-radio>
<el-radio label="style-3">{{ t('carouselSearchSwiperIndicatorStyle3') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('carouselSearchSwiperIndicatorAlign')">
<el-radio-group v-model="diyStore.editComponent.swiper.indicatorAlign">
<el-radio label="left">{{ t('alignLeft') }}</el-radio>
<el-radio label="center">{{ t('alignCenter') }}</el-radio>
<el-radio label="right">{{ t('alignRight') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('noColor')">
<el-color-picker v-model="diyStore.editComponent.swiper.indicatorColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('selectColor')">
<el-color-picker v-model="diyStore.editComponent.swiper.indicatorActiveColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<!-- <slot name="style"></slot> -->
</div>
<!-- 组件样式 -->
<!-- <slot name="style"></slot> -->
</div>
</template>
@ -317,19 +331,19 @@ import useDiyStore from '@/stores/modules/diy'
import { ref, reactive, watch, onMounted, nextTick } from 'vue'
import { ElTable } from 'element-plus'
import Sortable from 'sortablejs'
import { range,cloneDeep } from 'lodash-es'
import { range, cloneDeep } from 'lodash-es'
import { getDiyPageListByCarouselSearch } from '@/app/api/diy'
const diyStore = useDiyStore()
diyStore.editComponent.ignore = ['componentBgColor','componentBgUrl','marginTop','marginBottom','topRounded','bottomRounded','pageBgColor','marginBoth'] //
diyStore.editComponent.ignore = ['componentBgColor', 'componentBgUrl', 'marginTop', 'marginBottom', 'topRounded', 'bottomRounded', 'pageBgColor', 'marginBoth'] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
diyStore.value[index].search.hotWord.list.forEach((item: any) => {
if(item.text == ''){
if (item.text == '') {
res.code = false
res.message = t('carouselSearchHotWordTextPlaceholder')
return res
@ -337,7 +351,7 @@ diyStore.editComponent.verify = (index: number) => {
});
diyStore.value[index].tab.list.forEach((item: any) => {
if(item.text == ''){
if (item.text == '') {
res.code = false
res.message = t('carouselSearchTabCategoryTextPlaceholder')
return res
@ -349,9 +363,9 @@ diyStore.editComponent.verify = (index: number) => {
// }
});
if(diyStore.value[index].swiper.control){
if (diyStore.value[index].swiper.control) {
diyStore.value[index].swiper.list.forEach((item: any) => {
if(item.imageUrl == ''){
if (item.imageUrl == '') {
res.code = false
res.message = t('imageUrlTip')
return res
@ -376,7 +390,7 @@ const showSearchStyle = () => {
selectSearchStyle.value = diyStore.editComponent.search.style;
}
const changeSearchStyle = (item:any) => {
const changeSearchStyle = (item: any) => {
selectSearchStyle.title = item.title;
selectSearchStyle.value = item.value;
}
@ -415,7 +429,8 @@ diyStore.editComponent.swiper.list.forEach((item: any) => {
})
const activeNames = ref(['tab', 'swiper'])
const handleChange = (val: string[]) => {}
const handleChange = (val: string[]) => {
}
onMounted(() => {
loadDiyPageList()
@ -424,11 +439,11 @@ onMounted(() => {
const addHotWordItem = () => {
diyStore.editComponent.search.hotWord.list.push({
id: diyStore.generateRandom(),
text : '关键词',
text: '关键词',
})
}
const tabClear = (index:any) => {
const tabClear = (index: any) => {
diyStore.editComponent.tab.list[index].diy_id = 0;
diyStore.editComponent.tab.list[index].diy_title = '';
}
@ -436,10 +451,10 @@ const tabClear = (index:any) => {
const addTabItem = () => {
diyStore.editComponent.tab.list.push({
id: diyStore.generateRandom(),
text : '分类名称', // 4
source : 'diy_page', // diy_page
diy_id : '',
diy_title : ''
text: '分类名称', // 4
source: 'diy_page', // diy_page
diy_id: '',
diy_title: ''
})
}
@ -507,8 +522,7 @@ const diyPageTable = reactive({
total: 0,
loading: true,
data: [],
searchParam: {
}
searchParam: {}
})
const diyPageTableRef = ref<InstanceType<typeof ElTable>>()
@ -552,7 +566,7 @@ const loadDiyPageList = (page: number = 1) => {
}
//
let currDiyPage:any = {}
let currDiyPage: any = {}
let currTabIndexForDiyPage = 0;
const handleCurrentDiyPageChange = (val: string | any[]) => {
currDiyPage = val
@ -564,7 +578,7 @@ const saveDiyPageId = () => {
diyPageShowDialog.value = false
}
const diyPageShowDialogOpen = (index:any) => {
const diyPageShowDialogOpen = (index: any) => {
diyPageShowDialog.value = true
currTabIndexForDiyPage = index;
if (currDiyPage) {
@ -593,28 +607,28 @@ const addImageAd = () => {
})
}
const selectImg = (url:string) => {
const selectImg = (url: string) => {
handleHeight(true)
}
const changeSwiperStyle = (value:any) => {
const changeSwiperStyle = (value: any) => {
handleHeight(true)
}
//
const handleHeight = (isCalcHeight:boolean = false)=> {
const handleHeight = (isCalcHeight: boolean = false) => {
diyStore.editComponent.swiper.list.forEach((item: any, index: number) => {
const image = new Image()
image.src = img(item.imageUrl)
image.onload = async () => {
image.onload = async() => {
item.imgWidth = image.width
item.imgHeight = image.height
//
if (isCalcHeight && index == 0) {
const ratio = item.imgHeight / item.imgWidth
if(diyStore.editComponent.swiper.swiperStyle == 'style-1') {
if (diyStore.editComponent.swiper.swiperStyle == 'style-1') {
item.width = 375 * 0.92 // 0.92
}else{
} else {
item.width = 355
}
item.height = item.width * ratio
@ -630,12 +644,13 @@ defineExpose({})
<style lang="scss" scoped></style>
<style lang="scss">
.select-diy-page-input .el-input__inner{
cursor: pointer;
}
.collapse-wrap{
.el-collapse-item__header{
font-size: 16px;
}
}
.select-diy-page-input .el-input__inner {
cursor: pointer;
}
.collapse-wrap {
.el-collapse-item__header {
font-size: 16px;
}
}
</style>

View File

@ -2,7 +2,7 @@
<el-dialog v-model="dialogThemeVisible" title="编辑色调" width="850px" align-center destroy-on-close="true">
<el-form :model="openData" label-width="150px" :rules="formRules">
<el-form-item label="色调名称" prop="title">
<el-input v-model="openData.title" placeholder="请输入色调名称" maxlength="15" class="!w-[250px]" :disabled="openData.id != ''" />
<el-input v-model="openData.title" placeholder="请输入色调名称" maxlength="15" class="!w-[250px]" :disabled="openData.id != ''" @keydown.enter.native.prevent />
</el-form-item>
</el-form>

View File

@ -238,8 +238,8 @@ route.query.title = route.query.title || ''
route.query.back = route.query.back || '/admin/diy/list'
const backPath = route.query.back
const template = ref('');
const oldTemplate = ref('');
const template = ref('')
const oldTemplate = ref('')
const wapUrl = ref('')
const wapDomain = ref('')
const wapPreview = ref('')
@ -273,7 +273,7 @@ const originData = reactive({
const isChange = ref(true) // truefalse
const goBack = () => {
if (isChange.value) {
location.href = `${location.origin}${backPath}`;
location.href = `${location.origin}${backPath}`
router.push(backPath)
} else {
//
@ -287,7 +287,7 @@ const goBack = () => {
autofocus: false
}
).then(() => {
location.href = `${location.origin}${backPath}`;
location.href = `${location.origin}${backPath}`
}).catch(() => {
})
}
@ -328,7 +328,7 @@ watch(
)
//
const changeTemplatePage = (value:any)=> {
const changeTemplatePage = (value: any) => {
//
if (diyStore.value.length) {
ElMessageBox.confirm(t('changeTemplatePageTips'), t('warning'), {
@ -337,7 +337,7 @@ const changeTemplatePage = (value:any)=> {
type: 'warning'
}).then(() => {
diyStore.changeCurrentIndex(-99)
diyStore.init(); //
diyStore.init() //
if (value) {
let data = cloneDeep(templatePages[value].data);
diyStore.global = data.global;
@ -349,13 +349,13 @@ const changeTemplatePage = (value:any)=> {
}
}).catch(() => {
//
template.value = oldTemplate.value;
template.value = oldTemplate.value
});
} else {
diyStore.init(); //
diyStore.init() //
if (value) {
let data = cloneDeep(templatePages[value].data);
diyStore.global = data.global;
let data = cloneDeep(templatePages[value].data)
diyStore.global = data.global
if (data.value.length) {
diyStore.value = data.value
}
@ -444,7 +444,7 @@ initPage({
}
}
loadDiyTemplatePages(data.type);
loadDiyTemplatePages(data.type)
//
wapDomain.value = data.domain_url.wap_domain

View File

@ -34,7 +34,7 @@
</el-tooltip>
</div>
</template>
<el-switch v-model="diyStore.editComponent.field.privacyProtection" />
<el-switch v-model="diyStore.editComponent.field.privacyProtection" :disabled ="diyStore.editComponent.addressFormat != 'province/city/district/address'" />
<div class="text-sm text-gray-400">{{ t('提交后自动隐藏地址,仅管理员可查看') }}</div>
</el-form-item>
</el-form>
@ -55,7 +55,7 @@
<script lang="ts" setup>
import { t } from '@/lang'
import { ref } from 'vue'
import { ref,watch } from 'vue'
import useDiyStore from '@/stores/modules/diy'
const diyStore = useDiyStore()
@ -66,6 +66,15 @@ diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
return res
}
watch(
() => diyStore.editComponent.addressFormat,
(newVal) => {
if (newVal !== 'province/city/district/address') {
diyStore.editComponent.field.privacyProtection = false
}
},
{ immediate: true }
)
defineExpose({})

View File

@ -4,7 +4,147 @@
<!-- 表单组件 字段内容设置 -->
<slot name="field"></slot>
todo 此处编写表格组件的属性
<el-form label-width="100px" class="px-[10px]" @submit.prevent>
<el-form-item :label="t('列设置')">
<div ref="imageBoxRef">
<div v-for="(item, index) in diyStore.editComponent.columnList" :key="item.id"
class="border-b-[1px] border-[#e0e0e0] py-1">
<div class="flex items-center justify-between">
<div class="flex">
<span :class="['iconfont', 'ml-[5px]', 'cursor-pointer', getIconClass(item.type)]"></span>
<el-input v-model="item.name" class="input-style" :input-style="{ boxShadow: 'none' }"
:placeholder="t('请输入列名')" />
</div>
<div class="flex">
<span v-if="diyStore.editComponent.columnList.length > 1" @click="removeOption(index)"
class="cursor-pointer ml-[5px] nc-iconfont nc-icon-shanchu-yuangaizhiV6xx"></span>
<span class="cursor-pointer ml-[5px] nc-iconfont nc-icon-xiaV6xx"></span>
</div>
</div>
<div v-if="item.type == 'radio'" class="flex">
<div class="text-[#999] mr-3" >{{ item.options?.length || 0 }}个选项</div>
<span class="text-primary cursor-pointer mr-[10px]" @click="openRadioDialog(item, index)">{{ t('编辑') }}</span>
</div>
<div v-if="item.type == 'date'" class="flex">
<span class="text-primary cursor-pointer mr-[10px]" @click="openRadioDialog(item, index)">{{ t('设置日期格式') }}</span>
</div>
<div v-if="item.type == 'address'" class="flex">
<div class="text-[#999] mr-3">精确到详细地址</div>
<span class="text-primary cursor-pointer mr-[10px]" @click="openRadioDialog(item, index)">{{ t('设置') }}</span>
</div>
</div>
</div>
<el-popover placement="bottom" :width="50" trigger="hover">
<template #reference>
<span class="text-primary cursor-pointer mr-[10px]">{{ t('添加') }}</span>
</template>
<div v-for="(item, index) in columnTypeOptions" :key="index" @click="addOption(item)"
class="cursor-pointer hover:bg-[#d1e1ff] rounded text-center">
<div class="py-1 text-[var(--el-text-color-primary]">{{ item.label }}</div>
</div>
</el-popover>
</el-form-item>
<el-form-item :label="t('是否自增')">
<el-switch v-model="diyStore.editComponent.autoIncrementControl" />
</el-form-item>
<el-form-item :label="t('填写限制')" v-if="diyStore.editComponent.autoIncrementControl">
<div class="flex items-center">
<span>默认显示</span>
<el-input v-model="diyStore.editComponent.writeLimit.default" class="input-short" :placeholder="t('')" />
<span></span>
</div>
<div class="flex items-center my-1">
<span>最少填写</span>
<el-input v-model="diyStore.editComponent.writeLimit.min" class="input-short" :placeholder="t('')" />
<span></span>
</div>
<div class="flex items-center">
<span>最多填写</span>
<el-input v-model="diyStore.editComponent.writeLimit.max" class="input-short" :placeholder="t('')" />
<span></span>
</div>
</el-form-item>
<el-form-item :label="t('按钮名称')" v-if="diyStore.editComponent.autoIncrementControl">
<el-input v-model="diyStore.editComponent.btnText" :placeholder="t('请输入按钮名称')" />
</el-form-item>
</el-form>
<!-- 单选项 -->
<!-- <el-dialog v-model="radioDialogVisible" :title="t('设置单选项')" width="500">
<div v-if="activeColumnTemp.type == 'radio'">
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('选项名称')">
<el-input v-model="activeColumnTemp.name" :input-style="{ boxShadow: 'none' }" />
</el-form-item>
<el-form-item :label="t('设置选项')">
<div ref="radioBoxRef">
<div v-for="(opt, idx) in activeColumnTemp.options" :key="opt.id">
<div class="flex items-center justify-between mb-2">
<div class="flex-1">
<el-input v-model="opt.label" :input-style="{ boxShadow: 'none' }"
:placeholder="t('请输入')" />
</div>
<span v-if="activeColumnTemp.options.length > 1" @click="removeOptionItem(idx)"
class="cursor-pointer ml-[5px] nc-iconfont nc-icon-shanchu-yuangaizhiV6xx"></span>
<span class="cursor-pointer ml-[5px] nc-iconfont nc-icon-iconpaixu1"></span>
</div>
</div>
<span class="text-primary cursor-pointer mr-[10px]" @click="addOptionItem">{{ t('添加选项') }}</span>
<span class="text-primary cursor-pointer mr-[10px]" @click="addOtherOption">{{ t('添加其它项') }}</span>
<el-popover :visible="visible" placement="bottom" :width="300">
<p class="mb-[5px]">{{ t('addMultipleOption') }}</p>
<p class="text-[#888] text-[12px] mb-[5px]">{{ t('addOptionTips') }}</p>
<el-input v-model.trim="optionsValue" type="textarea" clearable maxlength="200"
show-word-limit />
<div class="mt-[10px] text-right">
<el-button size="small" text @click="visible = false">{{ t('cancel') }}</el-button>
<el-button size="small" type="primary" @click="batchAddOptions">{{t('confirm')}}</el-button>
</div>
<template #reference>
<span class="text-primary cursor-pointer"
@click="visible = true">{{ t('addMultipleOption') }}</span>
</template>
</el-popover>
</div>
</el-form-item>
</el-form>
</div>
<div v-else-if="activeColumnTemp.type == 'date'">
<el-form>
<el-form-item :label="t('dataFormat')">
<el-radio-group v-model="activeColumnTemp.dateFormat" class="!block">
<el-radio class="!block" label="YYYY年M月D日">{{ dateFormat.format1 }}</el-radio>
<el-radio class="!block" label="YYYY-MM-DD">{{ dateFormat.format2 }}</el-radio>
<el-radio class="!block" label="YYYY/MM/DD">{{ dateFormat.format3 }}</el-radio>
<el-radio class="!block" label="YYYY-MM-DD HH:mm">{{ dateFormat.format4 }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<div v-else-if="activeColumnTemp.type == 'address'">
<el-form>
<el-form-item :label="t('地址格式')">
<el-radio-group v-model="activeColumnTemp.addressFormat" class="!block">
<el-radio class="!block" label="province/city/district/address">{{ t('省/市/区/街道/详细地址') }}</el-radio>
<el-radio class="!block" label="province/city/district/street">{{ t('省/市/区/街道(镇)') }}</el-radio>
<el-radio class="!block" label="province/city/district">{{ t('省/市/区(县)') }}</el-radio>
<el-radio class="!block" label="province/city">{{ t('省/市') }}</el-radio>
<el-radio class="!block" label="province">{{ t('省') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="radioDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleDialogConfirm">确定</el-button>
</div>
</template>
</el-dialog> -->
<div>
</div>
<!-- 表单组件 其他设置 -->
<slot name="other"></slot>
@ -25,20 +165,248 @@
<script lang="ts" setup>
import { t } from '@/lang'
import { ref } from 'vue'
import Sortable from 'sortablejs'
import { ref, watch, onMounted, nextTick ,reactive, computed} from 'vue'
import useDiyStore from '@/stores/modules/diy'
import { range } from 'lodash-es'
const diyStore = useDiyStore()
diyStore.editComponent.ignore = ['componentBgUrl'] //
//
diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
// todo
return res
}
//
const columnTypeOptions = ref([
{ label: '单选项', value: 'radio' },
{ label: '文本', value: 'text' },
{ label: '数字', value: 'number' },
{ label: '手机号', value: 'mobile' },
{ label: '地址', value: 'address' },
{ label: '身份证', value: 'idcard' },
{ label: '性别', value: 'gender' },
{ label: '日期', value: 'date' }
])
const getIconClass = (type:any) => {
switch (type) {
case 'radio':
return 'icona-duihaopc30'
case 'text':
return 'icona-danhangwenben-1pc30'
case 'number':
return 'icona-shuzipc30-1'
case 'mobile':
return 'icona-shoujipc30'
case 'address':
return 'iconbiaotipc'
case 'idcard':
return 'icona-shenfenzhengpc30'
case 'gender':
return 'el-icon-s-opportunity'
case 'date':
return 'icona-riqipc30'
default:
return ''
}
}
const imageBoxRef = ref()
const generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2, 5)
//
const addOption = (item) => {
const newColumn: any = {
id: generateId(),
name: item.label,
type: item.value, //
value: '' //
}
// options
if (item.value === 'radio') {
newColumn.options = [
{ id: generateId(), label: '选项1' },
{ id: generateId(), label: '选项2' }
]
}
// dateFormat
if (item.value === 'date') {
newColumn.dateFormat = 'YYYY年M月D日' //
}
// addressFormat
if (item.value === 'address') {
newColumn.addressFormat = 'province/city/district/address' //
}
diyStore.editComponent.columnList.push(newColumn)
}
const removeOption = (index: number) => {
diyStore.editComponent.columnList.splice(index, 1)
}
onMounted(() => {
// nextTick(() => {
// if (diyStore.editComponent.columnList.length < 2) return;
// const sortable = Sortable.create(imageBoxRef.value, {
// group: 'item-wrap',
// animation: 200,
// onEnd: event => {
// const temp = diyStore.editComponent.columnList[event.oldIndex!]
// diyStore.editComponent.columnList.splice(event.oldIndex!, 1)
// diyStore.editComponent.columnList.splice(event.newIndex!, 0, temp)
// sortable.sort(
// range(diyStore.editComponent.columnList.length).map(value => {
// return value.toString()
// })
// )
// }
// })
// })
console.log(diyStore.editComponent.columnList);
})
const activeColumn = ref<any>({}) //
const activeColumnTemp = ref<any>({}) //
const activeRadioIndex = ref(0) //
const radioDialogVisible = ref(false)
const radioBoxRef = ref()
const optionsValue = ref('')
const visible = ref(false)
const dateFormat: any = reactive({
format1: '',
format2: '',
format3: '',
format4: ''
});
const openRadioDialog = (item, index) => {
activeRadioIndex.value = index // 便
activeColumn.value = item
activeColumnTemp.value = JSON.parse(JSON.stringify(item)) //
if(item.type == 'radio'){
if (!activeColumnTemp.value.options) activeColumnTemp.value.options = []
radioDialogVisible.value = true
// nextTick(() => initRadioSortable()) //
}else if(item.type == 'date'){
//
const today = new Date();
let year = today.getFullYear();
let month = String(today.getMonth() + 1).padStart(2, '0');
let day = String(today.getDate()).padStart(2, '0');
const hours = String(today.getHours()).padStart(2, '0');
const minutes = String(today.getMinutes()).padStart(2, '0');
dateFormat.format1 = `${ year }${ month }${ day }`;
dateFormat.format2 = `${ year }-${ month }-${ day }`;
dateFormat.format3 = `${ year }/${ month }/${ day }`;
dateFormat.format4 = `${ year }-${ month }-${ day } ${ hours }:${ minutes }`;
radioDialogVisible.value = true
} else if(item.type == 'address'){
radioDialogVisible.value = true
}
}
//
// const initRadioSortable = () => {
// Sortable.create(radioBoxRef.value, {
// group: 'radio-option-wrap',
// animation: 200,
// draggable: '.drag-radio-item',
// onEnd: event => {
// const options = activeColumnTemp.value.options // temp
// const temp = options[event.oldIndex!]
// options.splice(event.oldIndex!, 1)
// options.splice(event.newIndex!, 0, temp)
// }
// })
// }
const handleDialogConfirm = () => {
console.log(activeColumnTemp.value);
diyStore.editComponent.columnList[activeRadioIndex.value] = JSON.parse(JSON.stringify(activeColumnTemp.value)) //
radioDialogVisible.value = false //
}
const addOptionItem = () => {
const newOption = { id: generateId(), label: '选项' + (activeColumnTemp.value.options.length + 1) }
activeColumnTemp.value.options.push(newOption)
}
const addOtherOption = () => {
const newOption = { id: generateId(), label: '其他' }
activeColumnTemp.value.options.push(newOption)
}
const removeOptionItem = (index: number) => {
activeColumnTemp.value.options.splice(index, 1)
}
//
const uniqueByKey = (arr: any, key: any) => {
const seen = new Set();
return arr.filter((item: any) => {
const serializedKey = JSON.stringify(item[key]);
return seen.has(serializedKey) ? false : seen.add(serializedKey);
});
}
//
const batchAddOptions = () => {
if (optionsValue.value.trim()) {
const newOptions = optionsValue.value.split(',').map((option: any) => {
return {
id: diyStore.generateRandom(),
label: option.trim()
};
}).filter((option: any) => option.label !== '');
//
const uniqueNewOptions = uniqueByKey(newOptions, 'label');
//
const filteredNewOptions = uniqueNewOptions.filter(newOption =>
!activeColumnTemp.value.options.some(existingOption => existingOption.label === newOption.label)
);
//
if (filteredNewOptions.length > 0) {
activeColumnTemp.value.options.push(...filteredNewOptions);
} else {
ElMessage({
message: t('errorTipsTwo'),
type: "warning",
});
}
optionsValue.value = '';
visible.value = false;
}
};
defineExpose({})
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
:deep(.input-style .el-input__wrapper) {
box-shadow: none !important;
}
.input-short{
width: 80px;
margin: 0 10px;
}
</style>

View File

@ -5,13 +5,25 @@
<!-- 表单组件 字段内容设置 -->
<slot name="field"></slot>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('上传方式')">
<el-form-item>
<template #label>
<div class="flex items-center">
<span class="mr-[3px]">{{ t('上传方式') }}</span>
<el-tooltip effect="light" :content="t('拍摄时长限制1分钟从相册上传不限制时长。')" placement="top">
<el-icon>
<QuestionFilled color="#999999" />
</el-icon>
</el-tooltip>
</div>
</template>
<el-radio-group v-model="diyStore.editComponent.uploadMode">
<el-radio label="shoot_and_album">{{ t('拍摄和相册') }}</el-radio>
<el-radio label="shoot_only">{{ t('只允许拍摄') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- 表单组件 其他设置 -->
<slot name="other"></slot>
</div>

View File

@ -50,7 +50,7 @@
</el-table-column>
<el-table-column prop="update_time" :label="t('updateTime')" min-width="120" />
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="100">
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="130">
<template #default="{ row }">
<div class="flex items-center justify-end">
<el-button type="primary" v-if="row.status == 1 && row.type=='DIY_FORM'" link @click="spreadEvent(row)">{{ t('promotion') }}</el-button>
@ -156,7 +156,7 @@
</el-dialog>
<!-- 推广弹出框 -->
<form-spread-popup ref="formSpreadPopupRef" />
<spread-popup ref="spreadPopupRef" />
<!-- 表单提交成功页弹出框 -->
<form-submit-popup ref="formSubmitPopupRef" @complete="loadDiyFormList" />
@ -181,9 +181,9 @@ import { useRoute, useRouter } from 'vue-router'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
import { img } from '@/utils/common'
import recordsDetail from '@/app/views/diy_form/records.vue'
import formSpreadPopup from '@/app/views/diy_form/components/form-spread-popup.vue'
import formSubmitPopup from '@/app/views/diy_form/components/form-submit-popup.vue'
import formWritePopup from '@/app/views/diy_form/components/form-write-popup.vue'
import spreadPopup from '@/components/spread-popup/index.vue'
const route = useRoute()
const router = useRouter()
@ -506,12 +506,19 @@ const shareEvent = async (formEl: FormInstance | undefined) => {
}
// 广
const formSpreadPopupRef: any = ref(null)
const spreadPopupRef = ref(null)
const spreadEvent = (data: any) => {
formSpreadPopupRef.value.show(data)
const pagePath = "/app/pages/index/diy_form"
const columnName = "form_id"
const columnValue = data.form_id
const title = "表单推广"
const folder = "diy_form"
spreadPopupRef.value?.show(pagePath, columnName, columnValue, title,folder)
}
//
const formSubmitPopupRef: any = ref(null)

View File

@ -106,7 +106,7 @@
</el-table-column>
<!-- <el-table-column fixed prop="create_time" :label="t('填表时间')" min-width="120" /> -->
<el-table-column fixed prop="create_time" :label="t('fillInFormTotal')" min-width="500">
<template #default="{ row }" @click="">
<template #default="{ row }">
{{ row.write_count }}
</template>
</el-table-column>
@ -155,9 +155,11 @@
<div class="flex mb-[10px]" v-for="(item, index) in formDetail" :key="index">
<div class="flex justify-end w-[100px]">{{ item.label }}</div>
<div class="flex ml-[20px]">
<div v-if="Array.isArray(item.text)" class="mr-[10px]" v-for="(textItem, i) in item.text" :key="i">
{{ textItem }}
</div>
<template v-if="Array.isArray(item.text)">
<div class="mr-[10px]" v-for="(textItem, i) in item.text" :key="i">
{{ textItem }}
</div>
</template>
<div v-else>{{ item.text }}</div>
</div>
</div>
@ -180,11 +182,10 @@
import { reactive, ref, defineAsyncComponent } from 'vue'
import { t } from '@/lang'
import { getDiyFormFieldsList, getDiyFormFieldStat, getFormRecords,getFormRecordsInfo,deleteFormRecords,getFormRecordsMember} from '@/app/api/diy_form'
import { useRouter, useRoute } from 'vue-router'
import { useRouter } from 'vue-router'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
const route = useRoute()
const router = useRouter()
const showDialog = ref(false)
const activeName = ref('detail_data')
@ -197,7 +198,7 @@ const handleClose = (done: () => void) => {
showDialog.value = false;
}
const formData = reactive({
const formData = reactive({
page: 1,
limit: 6,
total: 0,
@ -213,22 +214,22 @@ const formData = reactive({
const formFieldsList = ref([])
//
const getDiyFormFieldsListFn = (form_id:any)=>{
const getDiyFormFieldsListFn = (form_id: any) => {
getDiyFormFieldsList({
form_id,
order: 'field_id',
sort: 'asc'
}).then((res:any)=>{
}).then((res: any) => {
formFieldsList.value = res.data;
})
}
//
const formFieldsStat = ref([])
const getDiyFormFieldStatFn = (form_id:any)=>{
const getDiyFormFieldStatFn = (form_id: any) => {
getDiyFormFieldStat({
form_id
}).then((res:any)=>{
}).then((res: any) => {
formFieldsStat.value = res.data;
})
}
@ -257,7 +258,6 @@ const deleteEvent = (row: any) => {
form_id: row.form_id
}).then(() => {
initData();
}).catch(() => {
})
})
}
@ -273,18 +273,18 @@ const resetFormMember = (formEl: FormInstance | undefined) => {
getFormRecordsMemberFn()
}
const loadFormRecordsListFn= (page: number = 1)=>{
const loadFormRecordsListFn= (page: number = 1)=> {
formData.loading = true
formData.page = page
getFormRecords({
page: formData.page,
limit: formData.limit,
...formData.searchParam
}).then((res:any)=>{
}).then((res: any) => {
formData.loading = false
formData.data = res.data.data
formData.data.forEach((item:any)=>{
for (let key:any in item.recordsFieldList){
formData.data.forEach((item: any) => {
for (let key: any in item.recordsFieldList) {
if (modules[item.recordsFieldList[key].detailComponent]) {
item.recordsFieldList[key].detailComponent && (item.recordsFieldList[key].detailComponent = defineAsyncComponent(modules[item.recordsFieldList[key].detailComponent]))
}
@ -295,7 +295,7 @@ const loadFormRecordsListFn= (page: number = 1)=>{
formData.loading = false
})
}
const formMemberList = reactive({
const formMemberList = reactive({
page: 1,
limit: 10,
total: 0,
@ -303,7 +303,7 @@ const formMemberList = reactive({
data: [],
searchParam: {
keyword: '',
form_id: 0,
form_id: 0
}
})
@ -320,12 +320,12 @@ const getFormRecordsMemberFn = (page: number = 1) => {
formMemberList.loading = false;
}).catch((error) => {
formMemberList.loading = false;
});
})
}
//
const detailEvent = (member_id:number)=> {
let routeData = router.resolve(`/member/detail?id=${member_id}`)
const detailEvent = (member_id:number) => {
let routeData = router.resolve(`/member/detail?id=${ member_id }`)
window.open(routeData.href, ' blank');
}
@ -338,7 +338,7 @@ const setFormData = async (row: any = null) => {
initData();
}
const initData = () =>{
const initData = () => {
getFormRecordsMemberFn();
getDiyFormFieldStatFn(formId.value);
loadFormRecordsListFn()
@ -387,5 +387,4 @@ defineExpose({
margin-right: 10px;
}
}
</style>

View File

@ -81,18 +81,18 @@
<div class="mt-[10px]">
<el-table :data="orderTableData.data" size="large" class="table-top">
<el-table-column :label="t('memberInfo')" min-width="180" />
<el-table-column :label="t('cashOutMethod')" align="center" min-width="100" />
<el-table-column :label="t('cashOutInfo')" min-width="180" />
<el-table-column :label="t('applicationForWithdrawalAmount')" align="center" min-width="120" />
<el-table-column :label="t('actualTransferAmount')" align="center" min-width="120" />
<el-table-column :label="t('cashOutCommission')" align="center" min-width="110" />
<el-table-column :label="t('cashOutStatus')" align="center" min-width="150" />
<el-table-column :label="t('memberInfo')" min-width="180" />
<el-table-column :label="t('cashOutMethod')" align="center" min-width="100" />
<el-table-column :label="t('cashOutInfo')" min-width="180" />
<el-table-column :label="t('applicationForWithdrawalAmount')" align="center" min-width="120" />
<el-table-column :label="t('actualTransferAmount')" align="center" min-width="120" />
<el-table-column :label="t('cashOutCommission')" align="center" min-width="110" />
<el-table-column :label="t('cashOutStatus')" align="center" min-width="150" />
<el-table-column :label="t('applyTime')" align="center" min-width="160" />
<el-table-column :label="t('auditTime')" align="center" min-width="160" />
<el-table-column :label="t('transferTime')" align="center" min-width="160" />
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120" />
</el-table>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120" />
</el-table>
<div class="table-body min-h-[150px]" v-loading="orderTableData.loading">
<div v-if="!orderTableData.loading">
<template v-if="orderTableData.data.length">
@ -126,7 +126,7 @@
<span class="w-[70px] flex-shrink-0 text-right">{{t('account') }}</span>
<span>{{ row.transfer_account }}</span>
</div>
<div class="flex items-center" v-if="row.transfer_payment_code">
<div class="flex items-start" v-if="row.transfer_payment_code">
<span class="w-[70px] flex-shrink-0 text-right">{{ t('transferCode') }}</span>
<el-image :src="img(row.transfer_payment_code)" :preview-src-list="[img(row.transfer_payment_code)]" :hide-on-click-modal="true" class="w-[50px] h-[50px]"></el-image>
</div>
@ -189,9 +189,9 @@
</el-table-column>
</el-table>
<div v-if="item.remark" class="text-[14px] min-h-[30px] leading-[30px] px-3 bg-[#fff0e5] text-[#ff7f5b] mb-[10px] relative remark">
<span class="mr-[5px]">{{ t('notes') }}</span>
<span>{{ item.remark }}</span>
</div>
<span class="mr-[5px]">{{ t('notes') }}</span>
<span>{{ item.remark }}</span>
</div>
</div>
</template>
<el-empty v-else :image-size="1" :description="t('emptyData')" />

View File

@ -52,13 +52,11 @@
</template>
<script lang="ts" setup>
import { reactive, ref, computed } from 'vue'
import { reactive, ref, computed,defineEmits } from 'vue'
import { t } from '@/lang'
import { getPayRefundInfo, getRefundType, getRefundTransfer } from '@/app/api/pay'
import { FormInstance, ElMessage } from 'element-plus'
import { FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { img, filterNumber } from '@/utils/common'
import useAppStore from '@/stores/modules/app'
const route = useRoute()
const router = useRouter()
@ -71,7 +69,6 @@ let refundNo = '';
const refundList = ref([])
const formData: Record<string, any> = ref(null)
const handleClose = (done: () => void) => {
showDialog.value = false;
}
@ -109,6 +106,7 @@ const transferEvent = (data:any) => {
transferDialog.value = true
transferFormData.refund_no = data.refund_no
transferFormData.refund_money = data.money
transferFormData.voucher = ''
}
const initialFormData = {
@ -127,6 +125,7 @@ const formRules = computed(() => {
]
}
})
const emit = defineEmits(['loadPayRefundList'])
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
@ -140,6 +139,7 @@ const confirm = async (formEl: FormInstance | undefined) => {
transferDialog.value = false
refundList.value = []
getRefundListInfo(refundNo)
emit('loadPayRefundList')
}).catch(() => {
transferDialog.value = false
loading.value = false

View File

@ -72,8 +72,7 @@
</div>
</el-card>
<el-image-viewer :url-list="previewImageList" v-if="imageViewerShow" @close="imageViewerShow = false" :initial-index="0"
:zoom-rate="1" />
<el-image-viewer :url-list="previewImageList" v-if="imageViewerShow" @close="imageViewerShow = false" :initial-index="0" :zoom-rate="1" />
</template>
<script lang="ts" setup>
@ -136,13 +135,13 @@ loadPayList()
const passEvent = (row: AnyObject) => {
ElMessageBox.confirm(
t('passTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
t('passTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(({ value }) => {
payAuditPass(row.out_trade_no).then(() => {
loadPayList()

View File

@ -56,14 +56,13 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
import { getPayDetail, payAuditPass, payAuditRefuse } from '@/app/api/sys'
import { img } from '@/utils/common'
import { ElMessageBox } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const id: number = parseInt((route.query.id || 0))
const loading = ref(true)
@ -81,13 +80,13 @@ setFormData()
const passEvent = () => {
ElMessageBox.confirm(
t('passTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
t('passTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(({ value }) => {
payAuditPass(formData.value.out_trade_no).then(() => {
setFormData()

View File

@ -10,7 +10,14 @@
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="payRefundTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('refundNo')" prop="refund_no">
<el-input v-model.trim="payRefundTable.searchParam.refund_no" :placeholder="t('refundNoPlaceholder')" />
<el-input v-model.trim="payRefundTable.searchParam.refund_no"
:placeholder="t('refundNoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select v-model="payRefundTable.searchParam.status" clearable class="input-width">
<el-option :label="t('selectPlaceholder')" value="" />
<el-option :label="item" :value="key" v-for="(item, key) in refundStatusList" :key="key" />
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="payRefundTable.searchParam.create_time" type="datetimerange"
@ -36,9 +43,9 @@
<el-table-column prop="status_name" :label="t('status')" min-width="120" />
<el-table-column prop="create_time" :label="t('createTime')" min-width="160" />
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="infoEvent(row)">{{ t('info') }}</el-button>
</template>
<template #default="{ row }">
<el-button type="primary" link @click="infoEvent(row)">{{ t('info') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -49,22 +56,25 @@
</div>
</div>
</el-card>
<refund-detail ref="refundDetailDialog"></refund-detail>
<refund-detail @loadPayRefundList="handleMessage" ref="refundDetailDialog"></refund-detail>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getPayRefundPages } from '@/app/api/pay'
import { useRouter, useRoute } from 'vue-router'
import { getPayRefundPages ,getRefundStatus} from '@/app/api/pay'
import { useRoute } from 'vue-router'
import type { FormInstance } from 'element-plus'
import refundDetail from '@/app/views/finance/components/refund-detail.vue'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const refundStatusList = ref([])
const checkStatusList = async () => {
refundStatusList.value = await (await getRefundStatus()).data
}
checkStatusList()
const payRefundTable = reactive({
page: 1,
limit: 10,
@ -73,6 +83,7 @@ const payRefundTable = reactive({
data: [],
searchParam: {
refund_no: '',
status: '',
create_time: []
}
})
@ -99,6 +110,9 @@ const loadPayRefundList = (page: number = 1) => {
})
}
loadPayRefundList()
const handleMessage = () => {
loadPayRefundList()
}
const refundDetailDialog: Record<string, any> | null = ref(null)
const infoEvent = (res:any) => {
let data = {no: res.refund_no};

View File

@ -36,7 +36,7 @@
<div class="px-[20px] pb-[10px] font-bold mt-[40px]">{{ t('weapp') }}</div>
<el-form label-width="40px" class="px-[20px]">
<el-form-item label=" " v-if="weappConfig.qr_code">
<el-image class="w-[100px] h-[100px]" :src="img(weappConfig.qr_code)" />
<el-image class="w-[150px] h-[150px]" :src="img(weappConfig.qr_code)" />
</el-form-item>
<el-form-item label=" " v-else>
<span class="text-gray-400">{{ t('weappNotSet') }}</span>

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
<el-input class="input-width" v-model.trim="formData.continue_sign" @keyup="filterNumber($event)" :maxlength="3" clearable />
<span class="ml-[10px]">{{ t('day') }}</span>
</el-form-item>
<el-form-item :label="t('continueSign')" >
<el-form-item :label="t('continueSignAward')">
<div class="flex-1">
<div v-for="(item,index) in gifts" :key="index" class="mb-[15px]">
<component :is="item.component" v-model="formData[item.key]" ref="giftRefs" v-if="item.component" />
@ -38,6 +38,10 @@ const props = defineProps({
default: () => {
return {}
}
},
sign_period: {
type: Number,
default: 0
}
})
const emits = defineEmits(['update:modelValue'])
@ -94,6 +98,8 @@ const formRules = reactive<FormRules>({
callback(t('continueSignFormatError'))
} else if (value < 2 || value > 365) {
callback(t('continueSignBerweenDays'))
} else if (Number(value) > Number(props.sign_period)) {
callback(t('continueSignMustLessThanSignPeriod')) //
} else{
callback();
}
@ -126,13 +132,16 @@ const formRules = reactive<FormRules>({
const verify = async () => {
let verify = true
await formRef.value?.validate((valid) => {
verify = valid
})
if (!verify) return verify
for (let i = 0; i < giftRefs.value.length; i++) {
const item = giftRefs.value[i]
!await item.verify() && (verify = false)
}
await formRef.value?.validate((valid) => {
verify = valid
})
return verify
}

View File

@ -40,7 +40,7 @@
</span>
</div>
</el-col>
<template v-for="(item,index) in verifyContentData.fixed">
<el-col :span="8">
<div class="flex items-center mt-[15px]" v-if="item.title">
@ -150,7 +150,6 @@ const getVerifyDetailFn = async () => {
}
const setFormData = async (row: any = null) => {
console.log("setFormData",row);
code = row.code;
getVerifyDetailFn();
}

View File

@ -90,7 +90,7 @@
<!-- 连签奖励 -->
<el-dialog v-model="continueSignDialog" :title="t('continueSignTitle')" width="1200px" :destroy-on-close="true" v-if="formData.is_use">
<sign-continue ref="continueRef" v-model="continue_award" />
<sign-continue ref="continueRef" v-model="continue_award" :sign_period="formData.sign_period" />
<template #footer>
<span class="dialog-footer">
<el-button @click="continueSignDialog = false">{{ t('cancel') }}</el-button>
@ -141,7 +141,21 @@ const regExp: any = {
//
const formRules = reactive<FormRules>({
day_award: [
{ required: true, message: t('daySignAwardPlaceholder'), trigger: 'change' }
{
required: true,
trigger: 'change',
validator: (rule: any, value: any, callback: any) => {
let isVerify = false
daySignAwardText.value.forEach(item => {
item.is_use && (isVerify = true)
})
if (!isVerify) {
callback(t('daySignAwardPlaceholder'))
} else {
callback()
}
}
}
],
sign_period:[{
required: true,

View File

@ -17,8 +17,8 @@
<el-table-column :label="t('memberInfo')" min-width="120">
<template #default="{ row }">
<div class="flex items-center cursor-pointer " @click="toMember(row.member.member_id)" v-if="row.member">
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" alt="">
<img class="w-[50px] h-[50px] mr-[10px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" />
<img class="w-[50px] h-[50px] mr-[10px] rounded-full" v-else src="@/app/assets/images/member_head.png" />
<div class="flex flex-col">
<span>{{ row.member.nickname || '' }}</span>
<span>{{ row.member.mobile || '' }}</span>
@ -30,17 +30,16 @@
<el-table-column :label="t('verifyType')" min-width="120">
<template #default="{ row }">
<div class="flex flex-col">
<div v-for="(item, key) in row.verify_type_array" class="my-[3px]" :key="key">
{{ item.verify_type_name }}
</div>
<div v-for="(item, key) in row.verify_type_array" class="my-[3px]" :key="key">{{ item.verify_type_name }}</div>
</div>
</template>
</el-table-column>
<el-table-column :label="t('createTime')" prop="create_time" min-width="120" />
<el-table-column :label="t('createTime')" prop="create_time" min-width="120"/>
<el-table-column :label="t('operation')" fixed="right" align="right" width="100">
<el-table-column :label="t('operation')" fixed="right" align="right" width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
@ -48,8 +47,8 @@
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="verifierTable.page" v-model:page-size="verifierTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="verifierTable.total"
@size-change="loadVerifierList()" @current-change="loadVerifierList" />
layout="total, sizes, prev, pager, next, jumper" :total="verifierTable.total"
@size-change="loadVerifierList()" @current-change="loadVerifierList"/>
</div>
</div>
</el-card>
@ -57,13 +56,13 @@
<el-dialog v-model="showDialog" :title="t('addVerifier')" width="500px" :destroy-on-close="true">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="addLoading">
<el-form-item :label="t('member')" prop="member_id">
<el-select v-model="formData.member_id" filterable remote reserve-keyword clearable :placeholder="t('searchPlaceholder')" :remote-method="searchMember" :loading="searchLoading" class="input-width">
<el-option v-for="item in memberList" :key="item.member_id" :label="item.nickname" :value="item.member_id" />
<el-select v-model="formData.member_id" filterable remote reserve-keyword clearable @focus="handleSelectFocus" :disabled="isEditMode" :placeholder="t('searchPlaceholder')" :remote-method="searchMember" :loading="searchLoading" class="input-width">
<el-option v-for="item in memberList" :key="item.member_id" :label="item.nickname" :value="item.member_id"/>
</el-select>
</el-form-item>
<el-form-item :label="t('verifyType')" prop="verify_type">
<el-select v-model="formData.verify_type" multiple collapse-tags clearable :placeholder="t('verifyTypePlaceholder')" class="input-width">
<el-option v-for="(item, index) in verifyTypeList" :key="index" :label="item.name" :value="index" />
<el-select v-model="formData.verify_type" multiple clearable :placeholder="t('verifyTypePlaceholder')" class="input-width">
<el-option v-for="(item, index) in verifyTypeList" :key="index" :label="item.name" :value="index"/>
</el-select>
</el-form-item>
</el-form>
@ -79,10 +78,17 @@
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { reactive, ref, nextTick } from 'vue'
import { t } from '@/lang'
import { useRouter, useRoute } from 'vue-router'
import { getVerifierList, deleteVerifier, addVerifier, getVerifyTypeList } from '@/app/api/verify'
import {
getVerifierList,
deleteVerifier,
addVerifier,
getVerifyTypeList,
getVerifyInfo,
editVerifier
} from '@/app/api/verify'
import { getMemberList } from '@/app/api/member'
import { ElMessageBox, FormInstance } from 'element-plus'
import { img } from '@/utils/common'
@ -95,12 +101,15 @@ const showDialog = ref(false)
const addLoading = ref(false)
const formData: Record<string, any> = reactive({
member_id: '',
verify_type: '',
verify_type: ''
})
const formRules = reactive({
member_id: [
{ required: true, message: t('memberIdPlaceholder'), trigger: 'blur' }
],
verify_type: [
{ required: true, message: t('verifyTypePlaceholder'), trigger: 'blur' }
]
})
const formRef = ref<FormInstance>()
@ -137,10 +146,44 @@ loadVerifierList()
/**
* 添加核销员表
*/
const isEditMode = ref(false);
const addEvent = () => {
isEditMode.value = false
formData.member_id = ''
formData.id = ''
formData.verify_type = ''
showDialog.value = true
}
const editEvent = async (row: any) => {
isEditMode.value = true
formData.member_id = ''
formData.verify_type = ''
memberList.value = []
try {
const res = await getVerifyInfo(row.id)
memberList.value = [{
member_id: res.data.member.member_id,
nickname: res.data.member.nickname
}]
nextTick(() => {
formData.member_id = res.data.member.member_id
formData.verify_type = res.data.verify_type
formData.id = row.id
showDialog.value = true
})
} catch (error) {
}
}
const handleSelectFocus = () => {
if (isEditMode.value && formData.member_id && memberList.value.length === 0) {
searchMember('')
}
}
/**
* 删除核销员表
*/
@ -168,8 +211,8 @@ const addVerifiers = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
addLoading.value = true
addVerifier(formData).then(res => {
const api = formData.id ? editVerifier : addVerifier
api(formData).then(res => {
addLoading.value = false
showDialog.value = false
formData.member_id = ''
@ -193,7 +236,7 @@ const searchMember = (query: string) => {
getMemberList({ keyword: query }).then(res => {
memberList.value = res.data.data
searchLoading.value = false
}).catch()
})
} else {
memberList.value = []
searchLoading.value = false
@ -207,17 +250,16 @@ const verifyTypeList = ref<any>([])
const setVerifyTypeList = () => {
getVerifyTypeList().then(res => {
verifyTypeList.value = res.data
}).catch()
})
}
setVerifyTypeList();
setVerifyTypeList()
/**
* 会员详情
*/
const toMember = (member_id: number) => {
router.push(`/member/detail?id=${member_id}`)
router.push(`/member/detail?id=${ member_id }`)
}
</script>
<style lang="scss" scoped></style>

View File

@ -64,7 +64,7 @@
@size-change="loadRecordList()" @current-change="loadRecordList" />
</div>
</div>
<verify-detail ref="verifyDetailDialog" />
</el-card>
</div>
@ -73,14 +73,12 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
import { FormInstance } from 'element-plus'
import { getVerifyRecord, getVerifyTypeList, getVerifierSelect } from '@/app/api/verify'
import verifyDetail from '@/app/views/marketing/components/verify-detail.vue'
import { img } from '@/utils/common'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const recordTable = reactive({

View File

@ -9,7 +9,7 @@
</div>
</el-form-item>
<el-form-item :label="t('memberId')" >
<el-form-item :label="t('memberId')" >
<div class="input-width"> {{ formData.member.member_no }} </div>
</el-form-item>

View File

@ -9,7 +9,7 @@
</div>
</el-form-item>
<el-form-item :label="t('memberId')" >
<el-form-item :label="t('memberId')" >
<div class="input-width"> {{ formData.member.member_no }} </div>
</el-form-item>

View File

@ -135,7 +135,7 @@ import { t } from '@/lang'
import { img } from '@/utils/common'
import { getRegisterChannelType, getMemberList, getMemberLabelAll, editMemberStatus, deleteMember, getMemberLevelAll } from '@/app/api/member'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { useRoute } from 'vue-router'
import AddMember from '@/app/views/member/components/add-member.vue'
import detailMember from '@/app/views/member/components/detail-member.vue'
import EditMember from '@/app/views/member/components/edit-member.vue'
@ -205,7 +205,6 @@ const loadMemberList = (page: number = 1) => {
}
loadMemberList()
const router = useRouter()
const addMemberDialog: Record<string, any> | null = ref(null)
const editMemberDialog: Record<string, any> | null = ref(null)
const detailMemberDialog: Record<string, any> | null = ref(null)

View File

@ -33,9 +33,7 @@
<el-statistic :value="formData.point">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="text-[14px]">
{{ t('point') }}
</span>
<span class="text-[14px]">{{ t('point') }}</span>
<el-tooltip effect="dark" :content="t('adjust')" placement="top">
<el-icon @click="adjustPoint(formData)" class="ml-2 cursor-pointer" :size="12">
<EditPen color="#273CE2" />
@ -64,9 +62,7 @@
<el-statistic :value="formData.balance">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="text-[14px]">
{{ t('balance') }}
</span>
<span class="text-[14px]">{{ t('balance') }}</span>
<el-tooltip effect="dark" :content="t('adjust')" placement="top">
<el-icon @click="adjustBalance(formData)" class="ml-2 cursor-pointer" :size="12">
<EditPen color="#273CE2" />
@ -83,9 +79,7 @@
<div class="statistic-footer">
<div class="footer-item text-[14px] text-secondary">
<span>{{ t('accumulative') }}</span>
<span class="red ml-1">
{{ formData.balance_get }}
</span>
<span class="red ml-1">{{ formData.balance_get }}</span>
</div>
</div>
</div>
@ -95,9 +89,7 @@
<el-statistic :value="formData.growth">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="text-[14px]">
{{ t('growth') }}
</span>
<span class="text-[14px]">{{ t('growth') }}</span>
<!-- <el-tooltip effect="dark" :content="t('adjust')" placement="top">-->
<!-- <el-icon @click="adjustGrowth(formData)" class="ml-2 cursor-pointer" :size="12">-->
<!-- <EditPen color="#273CE2" />-->
@ -118,9 +110,7 @@
<el-statistic :value="formData.money" title="New transactions today">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="text-[14px]">
{{ t("money") }}
</span>
<span class="text-[14px]">{{ t("money") }}</span>
<el-tooltip effect="dark" :content="t('detail')" placement="top">
<el-icon @click="infoBalance(formData)" class="ml-2 cursor-pointer" :size="12">
<View />
@ -132,9 +122,7 @@
<div class="statistic-footer">
<div class="footer-item text-[14px] text-secondary">
<span>{{ t('accumulative') }}</span>
<span class="green ml-1">
{{ formData.money_get }}
</span>
<span class="green ml-1">{{ formData.money_get }}</span>
</div>
</div>
</div>
@ -144,9 +132,7 @@
<el-statistic :value="formData.commission" title="New transactions today">
<template #title>
<div style="display: inline-flex; align-items: center ">
<span class="text-[14px]">
{{ t("commission") }}
</span>
<span class="text-[14px]">{{ t("commission") }}</span>
<el-tooltip effect="dark" :content="t('detail')" placement="top">
<el-icon @click="infoCommission(formData)" class="ml-2 cursor-pointer" :size="12">
<View />
@ -158,9 +144,7 @@
<div class="statistic-footer">
<div class="footer-item text-[14px] text-secondary">
<span>{{ t('accumulative') }}</span>
<span class="green ml-1">
{{ formData.commission_get }}
</span>
<span class="green ml-1">{{ formData.commission_get }}</span>
</div>
</div>
</div>
@ -172,22 +156,16 @@
<el-card class="box-card !border-none" shadow="never">
<div class="flex items-center mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('urserName') }}</span>
<span class="text-[14px] text-[#666666]">
{{ formData.username || t('notAvailable') }}
</span>
<span class="text-[14px] text-[#666666]">{{ formData.username || t('notAvailable') }}</span>
</div>
<div class="flex items-center mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('nickname') }}</span>
<span class="text-[14px] text-[#666666]">
{{ formData.nickname || t('notAvailable') }}
</span>
<span class="text-[14px] text-[#666666]">{{ formData.nickname || t('notAvailable') }}</span>
</div>
<div class="flex items-center mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('mobile') }}</span>
<span class="text-[14px] text-[#666666]">
{{ formData.mobile || t('notAvailable') }}
</span>
<span class="text-[14px] text-[#666666]">{{ formData.mobile || t('notAvailable') }}</span>
</div>
<div class="flex items-center mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('memberLevel') }}</span>
@ -276,12 +254,10 @@ import { img } from '@/utils/common'
import PointEdit from '@/app/views/member/components/member-point-edit.vue'
import BalanceEdit from '@/app/views/member/components/member-balance-edit.vue'
import EditMember from '@/app/views/member/components/edit-member.vue'
import useAppStore from '@/stores/modules/app'
const route = useRoute()
const pageName = route.meta.title
const appStore = useAppStore()
const loading = ref(true)
//

View File

@ -1,11 +1,11 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 组件公共属性 -->
<slot name="common"></slot>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,11 +1,11 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 组件公共属性 -->
<slot name="common"></slot>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,11 +1,11 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 组件公共属性 -->
<slot name="common"></slot>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,22 +1,26 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 属性内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<div class="mb-[10px] text-sm text-primary">{{ t('needLoginTips') }}</div>
</div>
<div class="edit-attr-item-wrap">
<div class="mb-[10px] text-sm text-primary">{{ t('needLoginTips') }}</div>
</div>
<!-- 组件公共属性 -->
<slot name="common"></slot>
<!-- 组件公共属性 -->
<slot name="common"></slot>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('imgShape')">
<div class="flex items-center">
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] border-solid border-[1px] border-transparent rounded-[50%]" :class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'circle'}" @click="imgShapeChangeFn('circle')"></div>
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] ml-[25px] border-solid border-[1px] border-transparent" :class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'normal'}" @click="imgShapeChangeFn('normal')"></div>
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] border-solid border-[1px] border-transparent rounded-[50%]"
:class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'circle'}"
@click="imgShapeChangeFn('circle')"></div>
<div class="bg-[#DFDFDF] cursor-pointer w-[50px] h-[50px] ml-[25px] border-solid border-[1px] border-transparent"
:class="{'border-[var(--el-color-primary)]': posterStore.editComponent.shape == 'normal'}"
@click="imgShapeChangeFn('normal')"></div>
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script lang="ts" setup>
@ -27,7 +31,7 @@ import usePosterStore from '@/stores/modules/poster'
const posterStore = usePosterStore()
//
const imgShapeChangeFn = (data)=>{
const imgShapeChangeFn = (data) => {
posterStore.editComponent.shape = data;
}

View File

@ -1,21 +1,21 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('image')">
<upload-image v-model="posterStore.editComponent.value" :limit="1"/>
</el-form-item>
</el-form>
</div>
<!-- 属性内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('image')">
<upload-image v-model="posterStore.editComponent.value" :limit="1" />
</el-form-item>
</el-form>
</div>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
</template>
<script lang="ts" setup>
import { ref,watch } from 'vue'
import { ref, watch } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import usePosterStore from '@/stores/modules/poster'
@ -39,7 +39,7 @@ watch(
//
const image = new Image()
image.src = img(posterStore.editComponent.value)
image.onload = async () => {
image.onload = async() => {
posterStore.editComponent.imgWidth = image.width
posterStore.editComponent.imgHeight = image.height
}

View File

@ -1,15 +1,15 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 属性内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<div class="mb-[10px] text-sm text-primary">{{ t('needLoginTips') }}</div>
</div>
<div class="edit-attr-item-wrap">
<div class="mb-[10px] text-sm text-primary">{{ t('needLoginTips') }}</div>
</div>
<!-- 组件公共属性 -->
<slot name="common"></slot>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,39 +1,39 @@
<template>
<!-- 内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('pageContent') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('posterName')">
<el-input v-model.trim="posterStore.name" :placeholder="t('posterNamePlaceholder')" clearable maxlength="12" show-word-limit/>
</el-form-item>
<el-form-item :label="t('bgType')">
<el-radio-group v-model="posterStore.global.bgType">
<el-radio :label="'url'">{{ t('bgUrl') }}</el-radio>
<el-radio :label="'color'">{{ t('bgColor') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('bgUrl')" v-show="posterStore.global.bgType == 'url'">
<upload-image v-model="posterStore.global.bgUrl" :limit="1"/>
<div class="text-sm text-gray-400 mt-[10px]">{{ t('bgUrlTips') }}</div>
</el-form-item>
<el-form-item :label="t('bgColor')" v-show="posterStore.global.bgType == 'color'">
<el-color-picker v-model="posterStore.editComponent.bgColor" :predefine="posterStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('statusLabel')" class="display-block">
<el-switch v-model="posterStore.status" :active-value="1" :inactive-value="0"/>
<div class="text-sm text-gray-400">{{ t('statusTips') }}</div>
</el-form-item>
</el-form>
<!-- 内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('pageContent') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('posterName')">
<el-input v-model.trim="posterStore.name" :placeholder="t('posterNamePlaceholder')" clearable maxlength="12" show-word-limit />
</el-form-item>
<el-form-item :label="t('bgType')">
<el-radio-group v-model="posterStore.global.bgType">
<el-radio :label="'url'">{{ t('bgUrl') }}</el-radio>
<el-radio :label="'color'">{{ t('bgColor') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('bgUrl')" v-show="posterStore.global.bgType == 'url'">
<upload-image v-model="posterStore.global.bgUrl" :limit="1" />
<div class="text-sm text-gray-400 mt-[10px]">{{ t('bgUrlTips') }}</div>
</el-form-item>
<el-form-item :label="t('bgColor')" v-show="posterStore.global.bgType == 'color'">
<el-color-picker v-model="posterStore.editComponent.bgColor" :predefine="posterStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('statusLabel')" class="display-block">
<el-switch v-model="posterStore.status" :active-value="1" :inactive-value="0" />
<div class="text-sm text-gray-400">{{ t('statusTips') }}</div>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import { watch, ref } from 'vue'
import { ref } from 'vue'
import { img } from '@/utils/common'
import usePosterStore from '@/stores/modules/poster'

View File

@ -1,11 +1,11 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 属性内容 -->
<div class="content-wrap">
<!-- 组件公共属性 -->
<slot name="common"></slot>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,19 +1,19 @@
<template>
<!-- 属性内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('textSet') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('textLabel')">
<el-input v-model.trim="posterStore.editComponent.value" type="textarea" rows="4" :placeholder="t('textPlaceholder')" clearable maxlength="50" show-word-limit />
</el-form-item>
</el-form>
</div>
<!-- 属性内容 -->
<div class="content-wrap">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('textSet') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('textLabel')">
<el-input v-model.trim="posterStore.editComponent.value" type="textarea" rows="4" :placeholder="t('textPlaceholder')" clearable maxlength="50" show-word-limit />
</el-form-item>
</el-form>
</div>
<!-- 组件公共属性 -->
<slot name="common"></slot>
<!-- 组件公共属性 -->
<slot name="common"></slot>
</div>
</div>
</template>
<script lang="ts" setup>

View File

@ -1,9 +1,9 @@
<template>
<div class="overflow-hidden" :style="componentStyle"></div>
<div class="overflow-hidden" :style="componentStyle"></div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
const prop = defineProps({
value: {
@ -12,17 +12,17 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `background-color: ${prop.value.bgColor};`;
style += `width: ${prop.value.width}px;height: ${prop.value.height}px;`;
style += `background-color: ${ prop.value.bgColor };`;
style += `width: ${ prop.value.width }px;height: ${ prop.value.height }px;`;
let box: any = document.getElementById(prop.value.id)
if (box) {
style += `width:${box.offsetWidth}px;height:${box.offsetHeight}px;`;
style += `width:${ box.offsetWidth }px;height:${ box.offsetHeight }px;`;
}
return style;
})

View File

@ -1,9 +1,9 @@
<template>
<div class="overflow-hidden" :style="componentStyle">{{ data.value }}</div>
<div class="overflow-hidden" :style="componentStyle">{{ data.value }}</div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
import usePosterStore from '@/stores/modules/poster'
const posterStore = usePosterStore()
@ -15,27 +15,27 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `font-size: ${prop.value.fontSize}px;color: ${prop.value.fontColor};line-height: ${prop.value.lineHeight + prop.value.fontSize}px;`;
if(prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right'){
style += `text-align: ${prop.value.x};`;
style += `font-size: ${ prop.value.fontSize }px;color: ${ prop.value.fontColor };line-height: ${ prop.value.lineHeight + prop.value.fontSize }px;`;
if (prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right') {
style += `text-align: ${ prop.value.x };`;
}
if(prop.value.weight){
if (prop.value.weight) {
style += `font-weight: bold;`;
}
if(!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf'){
if (!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf') {
style += `font-family: poster_default_font;`;
}
let box: any = document.getElementById(prop.value.id)
if (box) {
style += `width:${box.offsetWidth}px;height:${box.offsetHeight}px;`;
}else{
style += `width:${prop.value.width}px;height:${prop.value.height}px;`;
style += `width:${ box.offsetWidth }px;height:${ box.offsetHeight }px;`;
} else {
style += `width:${ prop.value.width }px;height:${ prop.value.height }px;`;
}
return style;
})

View File

@ -1,9 +1,9 @@
<template>
<div class="overflow-hidden" :style="componentStyle">{{ data.value }}</div>
<div class="overflow-hidden" :style="componentStyle">{{ data.value }}</div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
import usePosterStore from '@/stores/modules/poster'
const posterStore = usePosterStore()
@ -15,27 +15,27 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `font-size: ${prop.value.fontSize}px;color: ${prop.value.fontColor};line-height: ${prop.value.lineHeight + prop.value.fontSize}px;`;
if(prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right'){
style += `text-align: ${prop.value.x};`;
style += `font-size: ${ prop.value.fontSize }px;color: ${ prop.value.fontColor };line-height: ${ prop.value.lineHeight + prop.value.fontSize }px;`;
if (prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right') {
style += `text-align: ${ prop.value.x };`;
}
if(prop.value.weight){
if (prop.value.weight) {
style += `font-weight: bold;`;
}
if(!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf'){
if (!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf') {
style += `font-family: poster_default_font;`;
}
let box: any = document.getElementById(prop.value.id)
if (box) {
style += `width:${box.offsetWidth}px;height:${box.offsetHeight}px;`;
}else{
style += `width:${prop.value.width}px;height:${prop.value.height}px;`;
style += `width:${ box.offsetWidth }px;height:${ box.offsetHeight }px;`;
} else {
style += `width:${ prop.value.width }px;height:${ prop.value.height }px;`;
}
return style;
})

View File

@ -1,11 +1,11 @@
<template>
<div class="pointer-events-none max-w-[720px]" :style="componentStyle">
<img src="@/app/assets/images/default_headimg_square.jpg" class="w-full h-full" />
</div>
<div class="pointer-events-none max-w-[720px]" :style="componentStyle">
<img src="@/app/assets/images/default_headimg_square.jpg" class="w-full h-full" />
</div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
import { img } from '@/utils/common'
const prop = defineProps({
@ -15,14 +15,14 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `width: ${prop.value.width}px;`;
if(prop.value.shape == 'circle'){
style += `width: ${ prop.value.width }px;`;
if (prop.value.shape == 'circle') {
style += `border-radius: 50%; overflow: hidden;`;
}
return style;

View File

@ -1,12 +1,12 @@
<template>
<div class="pointer-events-none max-w-[720px]" :style="componentStyle">
<img v-if="data.value" :src="img(data.value)" class="w-full h-full" />
<img v-else :src="img('static/resource/images/diy/crack_figure.png')" class="w-full h-full" />
</div>
<div class="pointer-events-none max-w-[720px]" :style="componentStyle">
<img v-if="data.value" :src="img(data.value)" class="w-full h-full" />
<img v-else :src="img('static/resource/images/diy/crack_figure.png')" class="w-full h-full" />
</div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
import { img } from '@/utils/common'
const prop = defineProps({
@ -16,13 +16,13 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `width: ${prop.value.width}px;`;
style += `width: ${ prop.value.width }px;`;
return style;
})

View File

@ -1,9 +1,9 @@
<template>
<div class="overflow-hidden" :style="componentStyle">会员昵称</div>
<div class="overflow-hidden" :style="componentStyle">会员昵称</div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
import usePosterStore from '@/stores/modules/poster'
const posterStore = usePosterStore()
@ -15,27 +15,27 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `font-size: ${prop.value.fontSize}px;color: ${prop.value.fontColor};line-height: ${prop.value.lineHeight + prop.value.fontSize}px;`;
if(prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right'){
style += `text-align: ${prop.value.x};`;
style += `font-size: ${ prop.value.fontSize }px;color: ${ prop.value.fontColor };line-height: ${ prop.value.lineHeight + prop.value.fontSize }px;`;
if (prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right') {
style += `text-align: ${ prop.value.x };`;
}
if(prop.value.weight){
if (prop.value.weight) {
style += `font-weight: bold;`;
}
if(!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf'){
if (!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf') {
style += `font-family: poster_default_font;`;
}
let box: any = document.getElementById(prop.value.id)
if (box) {
style += `width:${box.offsetWidth}px;height:${box.offsetHeight}px;`;
}else{
style += `width:${prop.value.width}px;height:${prop.value.height}px;`;
style += `width:${ box.offsetWidth }px;height:${ box.offsetHeight }px;`;
} else {
style += `width:${ prop.value.width }px;height:${ prop.value.height }px;`;
}
return style;
})

View File

@ -1,11 +1,11 @@
<template>
<div class="pointer-events-none max-w-[720px]" :style="componentStyle">
<img :src="img('static/resource/images/diy/qrcode.png')" class="w-full h-full" />
</div>
<div class="pointer-events-none max-w-[720px]" :style="componentStyle">
<img :src="img('static/resource/images/diy/qrcode.png')" class="w-full h-full" />
</div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
import { img } from '@/utils/common'
const prop = defineProps({
@ -15,13 +15,13 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `width: ${prop.value.width}px;`;
style += `width: ${ prop.value.width }px;`;
return style;
})

View File

@ -1,9 +1,9 @@
<template>
<div class="overflow-hidden" :style="componentStyle">{{ data.value }}</div>
<div class="overflow-hidden" :style="componentStyle">{{ data.value }}</div>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue'
import { ref, computed } from 'vue'
import usePosterStore from '@/stores/modules/poster'
const posterStore = usePosterStore()
@ -15,27 +15,27 @@ const prop = defineProps({
}
})
const data = computed(()=> {
const data = computed(() => {
return prop.value;
})
const componentStyle = computed(()=> {
const componentStyle = computed(() => {
var style = '';
style += `font-size: ${prop.value.fontSize}px;color: ${prop.value.fontColor};line-height: ${prop.value.lineHeight + prop.value.fontSize}px;`;
if(prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right'){
style += `text-align: ${prop.value.x};`;
style += `font-size: ${ prop.value.fontSize }px;color: ${ prop.value.fontColor };line-height: ${ prop.value.lineHeight + prop.value.fontSize }px;`;
if (prop.value.x == 'left' || prop.value.x == 'center' || prop.value.x == 'right') {
style += `text-align: ${ prop.value.x };`;
}
if(prop.value.weight){
if (prop.value.weight) {
style += `font-weight: bold;`;
}
if(!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf'){
if (!prop.value.fontFamily || prop.value.fontFamily == 'static/font/SourceHanSansCN-Regular.ttf') {
style += `font-family: poster_default_font;`;
}
let box: any = document.getElementById(prop.value.id)
if (box) {
style += `width:${box.offsetWidth}px;height:${box.offsetHeight}px;`;
}else{
style += `width:${prop.value.width}px;height:${prop.value.height}px;`;
style += `width:${ box.offsetWidth }px;height:${ box.offsetHeight }px;`;
} else {
style += `width:${ prop.value.width }px;height:${ prop.value.height }px;`;
}
return style;
})

View File

@ -82,8 +82,7 @@
:style="previewIframeStyle(item)"
:class="{ 'selected' : posterStore.currentIndex == index }"
@mousedown="posterStore.mouseDown($event,item.id,index)"
@click.stop="posterStore.changeCurrentIndex(index,item)"
>
@click.stop="posterStore.changeCurrentIndex(index,item)">
<component :is="modules['preview-' + item.path]" :value="item"/>
<span class="box1" @mousedown.stop="posterStore.resizeMouseDown($event,item, index)"></span>
<span class="box2" @mousedown.stop="posterStore.resizeMouseDown($event,item, index)"></span>
@ -299,7 +298,6 @@ const previewIframeStyle = (data: any)=>{
default:
style.left = data.x + 'px'
}
// console.log(data.x,data.y)
return style
}

View File

@ -1,363 +1,373 @@
<template>
<div class="main-container">
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef"
v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('printerSet') }}</h3>
<h3 class="panel-title !text-sm">{{ t('printerSet') }}</h3>
<el-form-item :label="t('printerName')" prop="printer_name">
<el-input v-model.trim="formData.printer_name" clearable :placeholder="t('printerNamePlaceholder')" class="input-width" maxlength="20" />
</el-form-item>
<el-form-item :label="t('printerName')" prop="printer_name">
<el-input v-model.trim="formData.printer_name" clearable :placeholder="t('printerNamePlaceholder')" class="input-width" maxlength="20" />
</el-form-item>
<el-form-item :label="t('brand')" prop="brand">
<el-select v-model="formData.brand" :placeholder="t('brandPlaceholder')" clearable>
<el-option v-for="(item,key) in brandList" :key="key" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="t('brand')" prop="brand">
<el-select v-model="formData.brand" :placeholder="t('brandPlaceholder')" clearable>
<el-option v-for="(item,key) in brandList" :key="key" :label="item" :value="key" />
</el-select>
</el-form-item>
<el-form-item :label="t('printerCode')" prop="printer_code">
<div>
<el-input v-model.trim="formData.printer_code" clearable :placeholder="t('printerCodePlaceholder')" class="input-width" maxlength="30" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('printerCodeTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('printerCode')" prop="printer_code">
<div>
<el-input v-model.trim="formData.printer_code" clearable :placeholder="t('printerCodePlaceholder')" class="input-width" maxlength="30" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('printerCodeTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('printerKey')" prop="printer_key">
<div>
<el-input v-model.trim="formData.printer_key" clearable :placeholder="t('printerKeyPlaceholder')" class="input-width" maxlength="30" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('printerKeyTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('printerKey')" prop="printer_key">
<div>
<el-input v-model.trim="formData.printer_key" clearable :placeholder="t('printerKeyPlaceholder')" class="input-width" maxlength="30" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('printerKeyTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('openId')" prop="open_id">
<div>
<el-input v-model.trim="formData.open_id" clearable :placeholder="t('openIdPlaceholder')" class="input-width" maxlength="30" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('openIdTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('openId')" prop="open_id">
<div>
<el-input v-model.trim="formData.open_id" clearable :placeholder="t('openIdPlaceholder')" class="input-width" maxlength="30" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('openIdTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('apikey')" prop="apikey">
<div>
<el-input v-model.trim="formData.apikey" clearable :placeholder="t('apikeyPlaceholder')" class="input-width" maxlength="60" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('apikeyTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('apikey')" prop="apikey">
<div>
<el-input v-model.trim="formData.apikey" clearable :placeholder="t('apikeyPlaceholder')" class="input-width" maxlength="60" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('apikeyTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('printWidth')" prop="print_width">
<el-radio-group v-model="formData.print_width">
<el-radio label="58mm">58mm</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('printWidth')" prop="print_width">
<el-radio-group v-model="formData.print_width">
<el-radio label="58mm">58mm</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('status')">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="t('status')">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</el-form-item>
</el-card>
<el-card v-for="(item,index) in printerType" :key="item.key" class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ item.title }}</h3>
</el-card>
<el-card v-for="(item,index) in printerType" :key="item.key" class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ item.title }}</h3>
<div class="flex mb-[10px] py-[8px] bg-[#F5F7F9] text-[14px]">
<div class="px-[12px] w-[200px]">{{ t('printTrigger') }}</div>
<div class="px-[12px] w-[100px]">{{ t('status') }}</div>
<div class="px-[12px] w-[250px]">{{ t('usePrintTemplate') }}</div>
<div class="px-[12px] w-[300px] flex-1" v-for="childItem in item.condition" :key="childItem.key">{{ childItem.title }}</div>
<div class="px-[12px] w-[200px]">{{ t('printNum') }}</div>
</div>
<div class="flex mb-[10px] py-[8px] bg-[#F5F7F9] text-[14px]">
<div class="px-[12px] w-[200px]">{{ t('printTrigger') }}</div>
<div class="px-[12px] w-[100px]">{{ t('status') }}</div>
<div class="px-[12px] w-[250px]">{{ t('usePrintTemplate') }}</div>
<div class="px-[12px] w-[300px] flex-1" v-for="childItem in item.condition" :key="childItem.key">{{ childItem.title }}</div>
<div class="px-[12px] w-[200px]">{{ t('printNum') }}</div>
</div>
<template v-if="item.trigger">
<div class="flex bg-[#f8f8f9] mb-[10px] py-[20px]" v-for="(triggerItem,triggerKey) in item.trigger" :key="triggerKey">
<template v-if="formData.value[item.key]['trigger_' + triggerKey]">
<div class="font-bold w-[200px] px-[12px]">{{ triggerItem }}</div>
<div class="w-[100px] px-[12px]">
<el-switch v-model="formData.value[item.key]['trigger_' + triggerKey].status" :active-value="1" :inactive-value="0" />
</div>
<div class="w-[250px] px-[12px]">
<el-select v-model="formData.value[item.key]['trigger_' + triggerKey].template_id" :placeholder="t('请选择小票打印模板')" clearable>
<el-option v-for="templateItem in templateList[item.key]" :key="templateItem.template_id" :label="templateItem.template_name" :value="templateItem.template_id" />
</el-select>
</div>
<template v-for="childItem in item.condition">
<div class="w-[300px] px-[12px] flex-1" v-if="childItem.type == 'checkbox'">
<el-checkbox-group v-model="formData.value[item.key]['trigger_' + triggerKey][childItem.key]">
<el-checkbox v-for="(checkboxItem, index) in childItem.list" :label="checkboxItem.value" :key="index">{{ checkboxItem.name }}</el-checkbox>
</el-checkbox-group>
</div>
</template>
<div class="w-[200px] px-[12px]">
<el-select v-model="formData.value[item.key]['trigger_' + triggerKey].print_num">
<el-option label="1联" :value="1" />
<el-option label="2联" :value="2" />
<el-option label="3联" :value="3" />
<el-option label="4联" :value="4" />
</el-select>
</div>
</template>
</div>
</template>
<template v-if="item.trigger">
<div class="flex bg-[#f8f8f9] mb-[10px] py-[20px]" v-for="(triggerItem,triggerKey) in item.trigger" :key="triggerKey">
<template v-if="formData.value[item.key]['trigger_' + triggerKey]">
<div class="font-bold w-[200px] px-[12px]">{{ triggerItem }}</div>
<div class="w-[100px] px-[12px]">
<el-switch v-model="formData.value[item.key]['trigger_' + triggerKey].status" :active-value="1" :inactive-value="0" />
</div>
<div class="w-[250px] px-[12px]">
<el-select v-model="formData.value[item.key]['trigger_' + triggerKey].template_id" :placeholder="t('请选择小票打印模板')" clearable>
<el-option v-for="templateItem in templateList[item.key]" :key="templateItem.template_id" :label="templateItem.template_name" :value="templateItem.template_id" />
</el-select>
</div>
<template v-for="childItem in item.condition">
<div class="w-[300px] px-[12px] flex-1" v-if="childItem.type == 'checkbox'">
<el-checkbox-group
v-model="formData.value[item.key]['trigger_' + triggerKey][childItem.key]">
<el-checkbox v-for="(checkboxItem, index) in childItem.list" :label="checkboxItem.value" :key="index">{{ checkboxItem.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</template>
<div class="w-[200px] px-[12px]">
<el-select v-model="formData.value[item.key]['trigger_' + triggerKey].print_num">
<el-option label="1联" :value="1" />
<el-option label="2联" :value="2" />
<el-option label="3联" :value="3" />
<el-option label="4联" :value="4" />
</el-select>
</div>
</template>
</div>
</template>
</el-card>
</el-form>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="repeat" @click="confirm(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="repeat" @click="confirm(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import { FormInstance, ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { deepClone } from '@/utils/common';
import { addPrinter, editPrinter,getPrinterInfo,getPrinterType,getPrinterBrand,getPrinterTemplateList } from '@/app/api/printer'
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import { FormInstance, ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { deepClone } from '@/utils/common';
import {
addPrinter,
editPrinter,
getPrinterInfo,
getPrinterType,
getPrinterBrand,
getPrinterTemplateList
} from '@/app/api/printer'
const route = useRoute()
const router = useRouter()
const repeat = ref(false)
const loading = ref(true)
const route = useRoute()
const router = useRouter()
const repeat = ref(false)
const loading = ref(true)
const pageName = route.meta.title
const pageName = route.meta.title
/**
* 表单数据
*/
const initialFormData:any = {
printer_id: route.query.printer_id || 0,
brand: '',
printer_name: '',
printer_code: '',
printer_key: '',
open_id: '',
apikey: '',
template_type: [],
trigger: [],
value: {},
print_width: '58mm',
status: 1,
/**
* 表单数据
*/
const initialFormData: any = {
printer_id: route.query.printer_id || 0,
brand: '',
printer_name: '',
printer_code: '',
printer_key: '',
open_id: '',
apikey: '',
template_type: [],
trigger: [],
value: {},
print_width: '58mm',
status: 1,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
printer_name: [
{ required: true, message: t('printerNamePlaceholder'), trigger: 'blur' },
],
brand: [
{ required: true, message: t('brandPlaceholder'), trigger: 'blur' },
],
printer_code: [
{ required: true, message: t('printerCodePlaceholder'), trigger: 'blur' },
],
printer_key: [
{ required: true, message: t('printerKeyPlaceholder'), trigger: 'blur' },
],
open_id: [
{ required: true, message: t('openIdPlaceholder'), trigger: 'blur' },
],
apikey: [
{ required: true, message: t('apikeyPlaceholder'), trigger: 'blur' },
]
}
})
const formData: Record<string, any> = reactive({ ...initialFormData })
const printerType = ref([])
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
printer_name: [
{ required: true, message: t('printerNamePlaceholder'), trigger: 'blur' },
],
brand: [
{ required: true, message: t('brandPlaceholder'), trigger: 'blur' },
],
printer_code: [
{ required: true, message: t('printerCodePlaceholder'), trigger: 'blur' },
],
printer_key: [
{ required: true, message: t('printerKeyPlaceholder'), trigger: 'blur' },
],
open_id: [
{ required: true, message: t('openIdPlaceholder'), trigger: 'blur' },
],
apikey: [
{ required: true, message: t('apikeyPlaceholder'), trigger: 'blur' },
]
}
})
const printerType = ref([])
const init = async ()=> {
await getPrinterType({}).then((res: any) => {
if (res.data) {
printerType.value = res.data;
for (let i = 0; i < printerType.value.length; i++) {
let item: any = printerType.value[i];
formData.value[item.key] = {};
let extendData: any = {};
for (let ci = 0; ci < item.condition.length; ci++) {
let condition = item.condition[ci];
extendData[condition.key] = [];
if (condition.type == 'checkbox') {
extendData[condition.key] = []; //
for (let k = 0; k < condition.list.length; k++) {
extendData[condition.key].push(condition.list[k].value);
}
}
}
for (let key in item.trigger) {
formData.value[item.key]['trigger_' + key] = {
status: 1,
template_id: '',
print_num: 1,
};
Object.assign(formData.value[item.key]['trigger_' + key], deepClone(extendData));
}
}
}
if (!formData.printer_id) {
loading.value = false
}
})
if (formData.printer_id) {
getPrinterInfo(formData.printer_id).then((res: any) => {
let data = res.data;
if (data) Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) {
if (key == 'value') {
for (let ck in formData[key]){
Object.assign(formData[key][ck],data[key][ck])
}
} else {
formData[key] = data[key]
}
}
})
loading.value = false
})
}
}
init()
const brandList = ref([])
getPrinterBrand({}).then((res: any) => {
brandList.value = res.data;
})
const templateList:any = ref({}) //
getPrinterTemplateList({}).then((res:any)=> {
const init = async() => {
await getPrinterType({}).then((res: any) => {
if (res.data) {
let data = res.data;
for (let i = 0; i < data.length; i++) {
let item = data[i];
if (templateList.value[item.template_type] == undefined) {
templateList.value[item.template_type] = []
printerType.value = res.data;
for (let i = 0; i < printerType.value.length; i++) {
let item: any = printerType.value[i];
formData.value[item.key] = {};
let extendData: any = {};
for (let ci = 0; ci < item.condition.length; ci++) {
let condition = item.condition[ci];
extendData[condition.key] = [];
if (condition.type == 'checkbox') {
extendData[condition.key] = []; //
for (let k = 0; k < condition.list.length; k++) {
extendData[condition.key].push(condition.list[k].value);
}
}
}
for (let key in item.trigger) {
formData.value[item.key]['trigger_' + key] = {
status: 1,
template_id: '',
print_num: 1,
};
Object.assign(formData.value[item.key]['trigger_' + key], deepClone(extendData));
}
templateList.value[item.template_type].push({
template_id: item.template_id,
template_name: item.template_name,
})
}
}
if (!formData.printer_id) {
loading.value = false
}
})
/**
* 确认
* @param formEl
*/
const confirm = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
if (printerType.value.length == 0) {
ElMessage({
type: 'warning',
message: t('printTypeEmpty')
if (formData.printer_id) {
getPrinterInfo(formData.printer_id).then((res: any) => {
let data = res.data;
if (data) Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) {
if (key == 'value') {
for (let ck in formData[key]) {
Object.assign(formData[key][ck], data[key][ck])
}
} else {
formData[key] = data[key]
}
}
})
loading.value = false
})
}
}
init()
const brandList = ref([])
getPrinterBrand({}).then((res: any) => {
brandList.value = res.data;
})
const templateList: any = ref({}) //
getPrinterTemplateList({}).then((res: any) => {
if (res.data) {
let data = res.data;
for (let i = 0; i < data.length; i++) {
let item = data[i];
if (templateList.value[item.template_type] == undefined) {
templateList.value[item.template_type] = []
}
templateList.value[item.template_type].push({
template_id: item.template_id,
template_name: item.template_name,
})
return;
}
}
})
let save = formData.printer_id ? editPrinter : addPrinter
/**
* 确认
* @param formEl
*/
const confirm = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async(valid) => {
if (valid) {
if (printerType.value.length == 0) {
ElMessage({
type: 'warning',
message: t('printTypeEmpty')
})
return;
}
let validateFlag = false;
let validateMessage = '';
let save = formData.printer_id ? editPrinter : addPrinter
for (let i = 0; i < printerType.value.length; i++) {
let item: any = printerType.value[i];
await formEl.validate(async(valid) => {
if (valid) {
for (let k = 0; k < Object.keys(item.trigger).length; k++) {
let triggerItem = Object.keys(item.trigger)[k];
if (formData.value[item.key]['trigger_' + triggerItem].status == 0) {
continue;
}
let validateFlag = false;
let validateMessage = '';
if (!formData.value[item.key]['trigger_' + triggerItem].template_id) {
validateFlag = true;
validateMessage = `请设置${ item.title }[${ item.trigger[triggerItem] }]的小票打印模板`
break;
}
for (let i = 0; i < printerType.value.length; i++) {
let item: any = printerType.value[i];
let isFail = false;
for (let ck = 0; ck < item.condition.length; ck++) {
let condition = item.condition[ck];
if (condition.type == 'checkbox') {
if (formData.value[item.key]['trigger_' + triggerItem][condition.key].length == 0) {
validateFlag = true;
validateMessage = `请设置${ item.title }[${ item.trigger[triggerItem] }]的${ condition.title }`
isFail = true;
break;
}
for (let k = 0; k < Object.keys(item.trigger).length; k++) {
let triggerItem = Object.keys(item.trigger)[k];
if (formData.value[item.key]['trigger_' + triggerItem].status == 0) {
continue;
}
if (!formData.value[item.key]['trigger_' + triggerItem].template_id) {
validateFlag = true;
validateMessage = `请设置${ item.title }[${ item.trigger[triggerItem] }]的小票打印模板`
break;
}
let isFail = false;
for (let ck = 0; ck < item.condition.length; ck++) {
let condition = item.condition[ck];
if (condition.type == 'checkbox') {
if (formData.value[item.key]['trigger_' + triggerItem][condition.key].length == 0) {
validateFlag = true;
validateMessage = `请设置${ item.title }[${ item.trigger[triggerItem] }]的${ condition.title }`
isFail = true;
break;
}
}
//
if (isFail) {
break;
}
}
//
if (validateFlag) {
if (isFail) {
break;
}
}
//
if (validateFlag) {
ElMessage({
type: 'warning',
message: validateMessage
})
return;
break;
}
formData.template_type = [];
formData.trigger = [];
for (let key in formData.value) {
for (let childKey in formData.value[key]) {
formData.trigger.push(key + '_' + childKey);
}
formData.template_type.push(key)
}
if (repeat.value) return
repeat.value = true
let data = formData
save(data).then(res => {
repeat.value = false
if (!formData.printer_id) {
router.push('/printer/list')
}
}).catch(err => {
repeat.value = false
})
}
})
}
const back = () => {
router.push('/printer/list')
}
if (validateFlag) {
ElMessage({
type: 'warning',
message: validateMessage
})
return;
}
formData.template_type = [];
formData.trigger = [];
for (let key in formData.value) {
for (let childKey in formData.value[key]) {
formData.trigger.push(key + '_' + childKey);
}
formData.template_type.push(key)
}
if (repeat.value) return
repeat.value = true
let data = formData
save(data).then(res => {
repeat.value = false
if (!formData.printer_id) {
router.push('/printer/list')
}
}).catch(err => {
repeat.value = false
})
}
})
}
const back = () => {
router.push('/printer/list')
}
</script>
<style lang="scss" scoped></style>

View File

@ -4,9 +4,7 @@
<div class="flex justify-between items-center mb-[5px]">
<span class="text-lg">{{pageName}}</span>
<el-button type="primary" @click="addEvent">
{{ t('addPrinter') }}
</el-button>
<el-button type="primary" @click="addEvent">{{ t('addPrinter') }}</el-button>
</div>
<el-tabs class="demo-tabs" model-value="/printer/list" @tab-change="handleClick">
@ -41,8 +39,8 @@
<el-table-column prop="status" :label="t('status')" min-width="80" :show-overflow-tooltip="true" >
<template #default="{ row }">
<el-tag type="success" v-if="row.status == 1" @click="modifyPrinterStatusEvent(row.printer_id, 0)" class="cursor-pointer">{{ t('statusOn') }}</el-tag>
<el-tag type="info" v-else @click="modifyPrinterStatusEvent(row.printer_id, 1)" class="cursor-pointer">{{ t('statusOff') }}</el-tag>
<el-tag type="success" v-if="row.status == 1" @click="modifyPrinterStatusEvent(row.printer_id, 0)" class="cursor-pointer">{{ t('statusOn') }}</el-tag>
<el-tag type="info" v-else @click="modifyPrinterStatusEvent(row.printer_id, 1)" class="cursor-pointer">{{ t('statusOff') }}</el-tag>
</template>
</el-table-column>
@ -73,6 +71,7 @@ import { t } from '@/lang'
import { getPrinterPageList, modifyPrinterStatus, deletePrinter,refreshPrinterToken,testPrint } from '@/app/api/printer'
import { ElMessageBox,FormInstance } from 'element-plus'
import { useRoute,useRouter } from 'vue-router'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const router = useRouter()
@ -89,8 +88,8 @@ const printerTable = reactive({
total: 0,
loading: true,
data: [],
searchParam:{
printer_name:''
searchParam: {
printer_name: ''
}
})
@ -106,17 +105,18 @@ const loadPrinterList = (page: number = 1) => {
getPrinterPageList({
page: printerTable.page,
limit: printerTable.limit,
...printerTable.searchParam
...printerTable.searchParam
}).then(res => {
printerTable.loading = false
printerTable.data = res.data.data
printerTable.total = res.data.total
setTablePageStorage(printerTable.page, printerTable.limit, printerTable.searchParam);
}).catch(() => {
printerTable.loading = false
})
}
loadPrinterList()
loadPrinterList(getTablePageStorage(printerTable.searchParam).page)
const isRepeat = ref(false)
@ -224,4 +224,4 @@ const refreshTokenEvent = (printer_id: any) => {
</script>
<style lang="scss" scoped>
</style>
</style>

View File

@ -1,349 +1,354 @@
<template>
<div class="main-container">
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
<div class="flex">
<div class="flex">
<div class="flex-1 mr-[20px] bg-[#fff]">
<div class="flex-1 mr-[20px] bg-[#fff]">
<el-card class="box-card !border-none" shadow="never">
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('templateInfoLabel') }}</h3>
<h3 class="panel-title !text-sm">{{ t('templateInfoLabel') }}</h3>
<el-form-item :label="t('templateName')" prop="template_name">
<el-input v-model.trim="formData.template_name" clearable :placeholder="t('templateNamePlaceholder')" class="input-width" maxlength="20" />
</el-form-item>
<el-form-item :label="t('templateName')" prop="template_name">
<el-input v-model.trim="formData.template_name" clearable :placeholder="t('templateNamePlaceholder')" class="input-width" maxlength="20" />
</el-form-item>
<el-form-item :label="t('templateType')" prop="template_type" v-if="printerType.length">
<el-radio-group v-model="formData.template_type">
<el-radio v-for="item in printerType" :key="item.key" :label="item.key" @change="handlePrintType">{{ item.title }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('templateType')" prop="template_type" v-if="printerType.length">
<el-radio-group v-model="formData.template_type">
<el-radio v-for="item in printerType" :key="item.key" :label="item.key" @change="handlePrintType">{{ item.title }}</el-radio>
</el-radio-group>
</el-form-item>
</el-card>
<el-card class="box-card !border-none" shadow="never" v-if="printerType.length">
</el-card>
<el-card class="box-card !border-none" shadow="never" v-if="printerType.length">
<h3 class="panel-title !text-sm">{{ t('templateEditLabel') }}</h3>
<h3 class="panel-title !text-sm">{{ t('templateEditLabel') }}</h3>
<div v-for="item in template" :key="item.key" class="bg-[#f8f8f9] mb-[20px] py-[20px] px-[40px] text-[14px]">
<h4 class="panel-title !text-sm">{{ item.title }}</h4>
<div v-for="(childItem,index) in item.list" :key="childItem.key" class="ml-[30px]" :style="{ 'margin-bottom' : item.list.length == (index + 1) ? '0' : '20px' }">
<div class="flex">
<el-checkbox v-model="formData.value[item.key][childItem.key].status" v-if="childItem.label" :label="childItem.label" :value="childItem.status" :true-value="1" :false-value="0" class="w-[180px] mr-[10px]" :disabled="childItem.disabled" />
<div v-for="item in template" :key="item.key" class="bg-[#f8f8f9] mb-[20px] py-[20px] px-[40px] text-[14px]">
<h4 class="panel-title !text-sm">{{ item.title }}</h4>
<div v-for="(childItem,index) in item.list" :key="childItem.key" class="ml-[30px]" :style="{ 'margin-bottom' : item.list.length == (index + 1) ? '0' : '20px' }">
<div class="flex">
<el-checkbox v-model="formData.value[item.key][childItem.key].status"
v-if="childItem.label" :label="childItem.label"
:value="childItem.status" :true-value="1" :false-value="0"
class="w-[180px] mr-[10px]" :disabled="childItem.disabled" />
<template v-if="childItem.type == 'input'">
<el-input v-model.trim="formData.value[item.key][childItem.key].value" clearable :placeholder="'请输入' + (childItem.placeholder ? childItem.placeholder : childItem.label)" class="input-width mr-[30px]" maxlength="32" />
</template>
<template v-if="childItem.type == 'input'">
<el-input v-model.trim="formData.value[item.key][childItem.key].value" clearable
:placeholder="'请输入' + (childItem.placeholder ? childItem.placeholder : childItem.label)"
class="input-width mr-[30px]" maxlength="32" />
</template>
<template v-if="childItem.type == 'checkbox'">
<el-checkbox-group v-model="formData.value[item.key][childItem.key].value" class="mr-[30px]">
<el-checkbox v-for="(checkboxItem, key) in childItem.list" :label="key" :key="key" :disabled="childItem.disabled">{{ checkboxItem }}</el-checkbox>
</el-checkbox-group>
</template>
<template v-if="childItem.type == 'checkbox'">
<el-checkbox-group v-model="formData.value[item.key][childItem.key].value" class="mr-[30px]">
<el-checkbox v-for="(checkboxItem, key) in childItem.list" :label="key" :key="key" :disabled="childItem.disabled">{{ checkboxItem }}</el-checkbox>
</el-checkbox-group>
</template>
<template v-if="childItem.type == 'select'">
<template v-if="childItem.type == 'select'">
<div class="leading-[30px] w-[50px] text-center text-[#707070] bg-[#d7d7d7] border-1 border-solid border-[#ededed]">{{ childItem.text }}</div>
<el-select v-model="formData.value[item.key][childItem.key].value" class="!w-[130px] mr-[30px]">
<el-option v-for="(item,key) in childItem.list" :key="key" :label="item" :value="key" />
</el-select>
<div class="leading-[30px] w-[50px] text-center text-[#707070] bg-[#d7d7d7] border-1 border-solid border-[#ededed]">{{ childItem.text }}</div>
<el-select v-model="formData.value[item.key][childItem.key].value" class="!w-[130px] mr-[30px]">
<el-option v-for="(item,key) in childItem.list" :key="key" :label="item" :value="key" />
</el-select>
</template>
</template>
<template v-if="childItem.fontSize">
<div class="flex mr-[30px]">
<div class="leading-[30px] w-[50px] text-center text-[#707070] bg-[#d7d7d7] border-1 border-solid border-[#ededed]">字号</div>
<el-select v-model="formData.value[item.key][childItem.key].fontSize" class="!w-[130px]">
<el-option label="小" value="normal" />
<!-- <el-option label="小" value="small" />-->
<el-option label="大" value="big" />
</el-select>
</div>
<template v-if="childItem.fontSize">
<div class="flex mr-[30px]">
<div class="leading-[30px] w-[50px] text-center text-[#707070] bg-[#d7d7d7] border-1 border-solid border-[#ededed]">字号</div>
<el-select v-model="formData.value[item.key][childItem.key].fontSize" class="!w-[130px]">
<el-option label="小" value="normal" />
<!--<el-option label="小" value="small" />-->
<el-option label="大" value="big" />
</el-select>
</div>
</template>
</template>
<template v-if="childItem.fontWeight">
<template v-if="childItem.fontWeight">
<div class="flex mr-[30px]">
<div class="leading-[30px] w-[50px] text-center text-[#707070] bg-[#d7d7d7] border-1 border-solid border-[#ededed]">粗细</div>
<el-select v-model="formData.value[item.key][childItem.key].fontWeight" class="!w-[130px]">
<el-option label="正常" value="normal" />
<el-option label="加粗" value="bold" />
</el-select>
</div>
</template>
<div class="flex mr-[30px]">
<div class="leading-[30px] w-[50px] text-center text-[#707070] bg-[#d7d7d7] border-1 border-solid border-[#ededed]">粗细</div>
<el-select v-model="formData.value[item.key][childItem.key].fontWeight" class="!w-[130px]">
<el-option label="正常" value="normal" />
<el-option label="加粗" value="bold" />
</el-select>
</div>
</template>
</div>
</div>
<div v-if="childItem.remark" class="text-[12px] text-[#b2b2b2] mt-[10px]">{{ childItem.remark }}</div>
<div v-if="childItem.remark" class="text-[12px] text-[#b2b2b2] mt-[10px]">{{ childItem.remark }}</div>
</div>
</div>
</div>
</div>
</el-card>
</div>
</el-card>
</div>
<el-card class="box-card !border-none w-[450px]" shadow="never">
<el-card class="box-card !border-none w-[450px]" shadow="never">
<h3 class="panel-title !text-sm">{{ t('preview') }}</h3>
<h3 class="panel-title !text-sm">{{ t('preview') }}</h3>
<!-- 动态加载组件 -->
<component :is="modules[previewPath]" :value="formData.value"/>
<!-- 动态加载组件 -->
<component :is="modules[previewPath]" :value="formData.value" />
</el-card>
</el-card>
</div>
</div>
</el-form>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="repeat" @click="confirm(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="repeat" @click="confirm(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import { FormInstance, ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { getPrinterType, addPrinterTemplate, editPrinterTemplate, getPrinterTemplateInfo } from '@/app/api/printer'
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import { FormInstance, ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { getPrinterType, addPrinterTemplate, editPrinterTemplate, getPrinterTemplateInfo } from '@/app/api/printer'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const repeat = ref(false)
const loading = ref(true)
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const repeat = ref(false)
const loading = ref(true)
/**
* 表单数据
*/
const initialFormData:any = {
template_id: route.query.template_id || 0,
template_type: '',
template_name: '',
value: {},
/**
* 表单数据
*/
const initialFormData: any = {
template_id: route.query.template_id || 0,
template_type: '',
template_name: '',
value: {},
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
template_name: [
{ required: true, message: t('templateNamePlaceholder'), trigger: 'blur' },
],
template_type: [
{ required: true, message: t('templateTypePlaceholder'), trigger: 'blur' },
]
}
})
const formData: Record<string, any> = reactive({ ...initialFormData })
//
const modulesFiles = import.meta.glob('./components/*.vue', { eager: true })
const addonModulesFiles = import.meta.glob('@/addon/**/views/printer/components/*.vue', { eager: true })
addonModulesFiles && Object.assign(modulesFiles, addonModulesFiles)
const formRef = ref<FormInstance>()
const modules: any = {}
for (const [key, value] of Object.entries(modulesFiles)) {
const moduleName: any = key.split('/').pop()
const name = moduleName.split('.')[0]
modules[name] = value.default
}
//
const formRules = computed(() => {
return {
template_name: [
{ required: true, message: t('templateNamePlaceholder'), trigger: 'blur' },
],
template_type: [
{ required: true, message: t('templateTypePlaceholder'), trigger: 'blur' },
]
const previewPath = ref('')
const printerType: any = ref([])
const template: any = ref([])
const init = async() => {
await getPrinterType({}).then((res: any) => {
if (res.data && res.data.length) {
printerType.value = res.data;
handlePrintType(printerType.value[0].key, Boolean(parseInt(formData.template_id)));
}
if (!formData.template_id) {
loading.value = false
}
})
//
const modulesFiles = import.meta.glob('./components/*.vue', { eager: true })
const addonModulesFiles = import.meta.glob('@/addon/**/views/printer/components/*.vue', { eager: true })
addonModulesFiles && Object.assign(modulesFiles, addonModulesFiles)
const modules:any = {}
for (const [key, value] of Object.entries(modulesFiles)) {
const moduleName:any = key.split('/').pop()
const name = moduleName.split('.')[0]
modules[name] = value.default
}
const previewPath = ref('')
const printerType:any = ref([])
const template:any = ref([])
const init = async ()=> {
await getPrinterType({}).then((res: any) => {
if (res.data && res.data.length) {
printerType.value = res.data;
handlePrintType(printerType.value[0].key, Boolean(parseInt(formData.template_id)));
}
if (!formData.template_id) {
loading.value = false
}
})
if(formData.template_id) {
getPrinterTemplateInfo(formData.template_id).then((res:any)=>{
let data = res.data;
if (data && Object.keys(data).length) {
Object.keys(formData).forEach((key: string) => {
if (key == 'value') {
for (let ck in formData[key]){
Object.assign(formData[key][ck],data[key][ck])
}
} else {
formData[key] = data[key]
}
})
loading.value = false
}else {
ElMessage({
type: 'warning',
duration: 1500,
message: t('printTemplateEmpty')
})
setTimeout(()=>{
back();
loading.value = false
}, 2000);
}
})
}
};
init();
//
const handlePrintType = (value: any, load: boolean = false) => {
for (let i = 0; i < printerType.value.length; i++) {
if (printerType.value[i].key == value) {
formData.template_type = printerType.value[i].key;
previewPath.value = printerType.value[i].path;
template.value = printerType.value[i].template;
break;
}
}
//
for (let key in formData.value) {
delete formData.value[key];
}
refreshTemplateData();
}
//
const refreshTemplateData = ()=> {
for (let i = 0; i < template.value.length; i++) {
let item: any = template.value[i];
formData.value[item.key] = {};
for (let k = 0; k < item.list.length; k++) {
let childItem = item.list[k];
formData.value[item.key][childItem.key] = {
type: childItem.type,
value: childItem.value,
status: childItem.status,
fontSize: childItem.fontSize,
fontWeight: childItem.fontWeight,
};
}
}
}
/**
* 确认
* @param formEl
*/
const confirm = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
if (printerType.value.length == 0) {
ElMessage({
type: 'warning',
message: t('printTypeEmpty')
})
return;
}
let save = formData.template_id ? editPrinterTemplate : addPrinterTemplate
await formEl.validate(async(valid) => {
if (valid) {
let validateFlag = false;
let validateMessage = '';
for (let i = 0; i < template.value.length; i++) {
let item: any = template.value[i];
let isFail = false;
for (let k = 0; k < item.list.length; k++) {
let childItem = item.list[k];
if (formData.value[item.key][childItem.key].status == 0) {
continue;
}
if (childItem.type == 'input') {
if (formData.value[item.key][childItem.key].value == '') {
validateFlag = true;
validateMessage = `请输入${ childItem.label }`;
isFail = true;
break;
}
} else if (childItem.type == 'select') {
if (formData.value[item.key][childItem.key].value == '') {
validateFlag = true;
validateMessage = `${ childItem.label }未设置[${ childItem.text }]`;
isFail = true;
break;
}
if (formData.template_id) {
getPrinterTemplateInfo(formData.template_id).then((res: any) => {
let data = res.data;
if (data && Object.keys(data).length) {
Object.keys(formData).forEach((key: string) => {
if (key == 'value') {
for (let ck in formData[key]) {
Object.assign(formData[key][ck], data[key][ck])
}
} else {
formData[key] = data[key]
}
//
if (isFail) {
break;
}
}
if (validateFlag) {
ElMessage({
type: 'warning',
message: validateMessage
})
return;
}
for (let i = 0; i < template.value.length; i++) {
let item: any = template.value[i];
for (let k = 0; k < item.list.length; k++) {
let childItem = item.list[k];
if (childItem.type == 'checkbox') {
if (formData.value[item.key][childItem.key].value.length) {
formData.value[item.key][childItem.key].status = 1;
} else {
formData.value[item.key][childItem.key].status = 0;
}
}
}
}
if (repeat.value) return
repeat.value = true
let data = formData
save(data).then(res => {
repeat.value = false
if (!formData.template_id) {
back()
}
}).catch(err => {
repeat.value = false
})
loading.value = false
} else {
ElMessage({
type: 'warning',
duration: 1500,
message: t('printTemplateEmpty')
})
setTimeout(() => {
back();
loading.value = false
}, 2000);
}
})
}
};
const back = () => {
router.push('/printer/template/list')
init();
//
const handlePrintType = (value: any, load: boolean = false) => {
for (let i = 0; i < printerType.value.length; i++) {
if (printerType.value[i].key == value) {
formData.template_type = printerType.value[i].key;
previewPath.value = printerType.value[i].path;
template.value = printerType.value[i].template;
break;
}
}
//
for (let key in formData.value) {
delete formData.value[key];
}
refreshTemplateData();
}
//
const refreshTemplateData = () => {
for (let i = 0; i < template.value.length; i++) {
let item: any = template.value[i];
formData.value[item.key] = {};
for (let k = 0; k < item.list.length; k++) {
let childItem = item.list[k];
formData.value[item.key][childItem.key] = {
type: childItem.type,
value: childItem.value,
status: childItem.status,
fontSize: childItem.fontSize,
fontWeight: childItem.fontWeight,
};
}
}
}
/**
* 确认
* @param formEl
*/
const confirm = async(formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
if (printerType.value.length == 0) {
ElMessage({
type: 'warning',
message: t('printTypeEmpty')
})
return;
}
let save = formData.template_id ? editPrinterTemplate : addPrinterTemplate
await formEl.validate(async(valid) => {
if (valid) {
let validateFlag = false;
let validateMessage = '';
for (let i = 0; i < template.value.length; i++) {
let item: any = template.value[i];
let isFail = false;
for (let k = 0; k < item.list.length; k++) {
let childItem = item.list[k];
if (formData.value[item.key][childItem.key].status == 0) {
continue;
}
if (childItem.type == 'input') {
if (formData.value[item.key][childItem.key].value == '') {
validateFlag = true;
validateMessage = `请输入${ childItem.label }`;
isFail = true;
break;
}
} else if (childItem.type == 'select') {
if (formData.value[item.key][childItem.key].value == '') {
validateFlag = true;
validateMessage = `${ childItem.label }未设置[${ childItem.text }]`;
isFail = true;
break;
}
}
}
//
if (isFail) {
break;
}
}
if (validateFlag) {
ElMessage({
type: 'warning',
message: validateMessage
})
return;
}
for (let i = 0; i < template.value.length; i++) {
let item: any = template.value[i];
for (let k = 0; k < item.list.length; k++) {
let childItem = item.list[k];
if (childItem.type == 'checkbox') {
if (formData.value[item.key][childItem.key].value.length) {
formData.value[item.key][childItem.key].status = 1;
} else {
formData.value[item.key][childItem.key].status = 0;
}
}
}
}
if (repeat.value) return
repeat.value = true
let data = formData
save(data).then(res => {
repeat.value = false
if (!formData.template_id) {
back()
}
}).catch(err => {
repeat.value = false
})
}
})
}
const back = () => {
router.push('/printer/template/list')
}
</script>
<style lang="scss" scoped>

View File

@ -68,6 +68,7 @@ import { t } from '@/lang'
import { getPrinterTemplatePageList, deletePrinterTemplate,getPrinterType } from '@/app/api/printer'
import { ElMessageBox,FormInstance } from 'element-plus'
import { useRoute,useRouter } from 'vue-router'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const router = useRouter()
@ -113,11 +114,12 @@ const loadPrinterTemplateList = (page: number = 1) => {
printerTemplateTable.loading = false
printerTemplateTable.data = res.data.data
printerTemplateTable.total = res.data.total
setTablePageStorage(printerTemplateTable.page, printerTemplateTable.limit, printerTemplateTable.searchParam);
}).catch(() => {
printerTemplateTable.loading = false
})
}
loadPrinterTemplateList()
loadPrinterTemplateList(getTablePageStorage(printerTemplateTable.searchParam).page)
/**
* 添加小票打印模板

View File

@ -154,7 +154,6 @@ const cancel = () => {
}
const setFormData = async (data: any = null) => {
console.log(data)
initData.value = cloneDeep(data)
loading.value = true
Object.assign(formData, initialFormData)

View File

@ -81,11 +81,7 @@ setFormData()
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({
site_name: [
{ required: true, message: t('siteNamePlaceholder'), trigger: 'blur' }
]
})
const formRules = reactive<FormRules>({})
/**
* 保存

View File

@ -83,7 +83,7 @@ import { t } from '@/lang'
import { img } from '@/utils/common'
import { getExportStatusList, getExportKeyList, getExportList, deleteExport } from '@/app/api/sys'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
@ -141,8 +141,6 @@ const loadExportList = (page: number = 1) => {
}
loadExportList()
const router = useRouter()
/**
* 下载导出报表
*/
@ -168,7 +166,6 @@ const deleteEvent = (id: number) => {
).then(() => {
deleteExport(id).then(() => {
loadExportList()
}).catch(() => {
})
})
}

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