mirror of
https://gitee.com/niucloud-team/niucloud-admin.git
synced 2026-03-17 11:13:43 +00:00
up admin
This commit is contained in:
parent
c337da9ea3
commit
d98c2dacc1
@ -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 })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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,7 +240,7 @@ 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 })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款方式
|
||||
*/
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
@ -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
|
||||
@ -65,7 +66,7 @@ export function getWeappVersionList(params: Record<string, any>) {
|
||||
* @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 }`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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 |
@ -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) {
|
||||
|
||||
240
admin/src/app/components/upgrade-log/index.vue
Normal file
240
admin/src/app/components/upgrade-log/index.vue
Normal 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>
|
||||
@ -1,58 +1,102 @@
|
||||
<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">
|
||||
<template v-if="upgradeContent">
|
||||
<!-- 检测服务是否到期 -->
|
||||
<template v-if="step == 1">
|
||||
<template v-for="(item, index) in upgradeContent.content">
|
||||
<div class="text-lg">
|
||||
本次升级将从<span class="font-bold">{{ upgradeContent.version }}</span>升级到<span class="font-bold">{{ upgradeContent.upgrade_version }}</span>版本
|
||||
<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>
|
||||
</div>
|
||||
<div class="mt-[10px]" v-if="upgradeContent.upgrade_version != upgradeContent.last_version">
|
||||
<el-alert type="info" show-icon>
|
||||
<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>
|
||||
当前最新版本为{{ upgradeContent.last_version }},您的服务{{ upgradeContent.expire_time ? `已于${upgradeContent.expire_time}到期` : '长期有效' }}。如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级
|
||||
<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>
|
||||
<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>
|
||||
</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 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="h-[60vh] flex flex-col" v-if="upgradeCheck && !upgradeTask">
|
||||
<div class="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>
|
||||
<span>{{ t("upgrade.path") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('upgrade.demand') }}</span>
|
||||
<span>{{ t("upgrade.demand") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('status') }}</span>
|
||||
<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>
|
||||
<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-if="item.status">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
@ -65,10 +109,14 @@
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('upgrade.write') }}</span>
|
||||
<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-if="item.status">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
@ -78,34 +126,89 @@
|
||||
</el-row>
|
||||
</div>
|
||||
</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 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>
|
||||
<!-- 是否备份选择 -->
|
||||
<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">
|
||||
<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" />
|
||||
</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>
|
||||
<el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" v-show="upgradeTask && upgradeTask.executed && !upgradeTask.executed.includes('cloudBuild')"/>
|
||||
</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 {
|
||||
await preUpgradeCheck(appKey).then(async ({ data }) => {
|
||||
upgradeCheck.value = data
|
||||
}
|
||||
}).catch()
|
||||
|
||||
if (uploading.value) active.value = 'upgrade'
|
||||
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'),
|
||||
{
|
||||
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(() => {
|
||||
}).then(() => {
|
||||
done()
|
||||
}).catch(() => { })
|
||||
})
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => showDialog.value, () => {
|
||||
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>
|
||||
|
||||
@ -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>()
|
||||
|
||||
@ -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"
|
||||
}
|
||||
@ -15,5 +15,5 @@
|
||||
"notHaveAuth": "还没有授权?去购买",
|
||||
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
|
||||
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
|
||||
"versionTips":"已经升级到最新版本"
|
||||
"versionTips": "已经升级到最新版本"
|
||||
}
|
||||
|
||||
18
admin/src/app/lang/zh-cn/app.upgrade.json
Normal file
18
admin/src/app/lang/zh-cn/app.upgrade.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"companyName": "授权主体",
|
||||
"siteAddress": "授权域名",
|
||||
"contactName": "授权联系人",
|
||||
"authCode": "授权码",
|
||||
"authSecret": "授权秘钥",
|
||||
"createTime": "授权时间",
|
||||
"expireTime": "到期时间",
|
||||
"authApp": "授权应用",
|
||||
"authAppKey": "应用标识",
|
||||
"siteAddressTips": "授权域名不匹配",
|
||||
"authCodePlaceholder": "请输入授权码",
|
||||
"authSecretPlaceholder": "请输入授权秘钥",
|
||||
"updateCode": "重新绑定",
|
||||
"notHaveAuth": "还没有授权?去购买",
|
||||
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
|
||||
"versionTips": "已经升级到最新版本"
|
||||
}
|
||||
@ -50,7 +50,7 @@
|
||||
"cashOutInfo":"收款方信息",
|
||||
"transferCode":"收款码",
|
||||
"realname":"真实姓名",
|
||||
"account":"账号",
|
||||
"account":"收款账号",
|
||||
"bankRealname":"持卡人姓名",
|
||||
"remark":"备注",
|
||||
"remarkPlaceholder":"请输入备注",
|
||||
|
||||
@ -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": "最新版本"
|
||||
}
|
||||
@ -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": "输入描述语…"
|
||||
}
|
||||
@ -1,21 +1,21 @@
|
||||
{
|
||||
"search":"搜索应用名称",
|
||||
"appName":"应用名/版本信息",
|
||||
"introduction":"简介",
|
||||
"type":"类型",
|
||||
"app":"应用",
|
||||
"addon":"插件",
|
||||
"noPlug":"暂无应用",
|
||||
"install":"安装",
|
||||
"unload":"卸载",
|
||||
"installLabel":"已安装",
|
||||
"uninstalledLabel":"未安装",
|
||||
"version":"版本",
|
||||
"title":"名称",
|
||||
"desc":"简介",
|
||||
"search": "搜索应用名称",
|
||||
"appName": "应用名/版本信息",
|
||||
"introduction": "简介",
|
||||
"type": "类型",
|
||||
"app": "应用",
|
||||
"addon": "插件",
|
||||
"noPlug": "暂无应用",
|
||||
"install": "安装",
|
||||
"unload": "卸载",
|
||||
"installLabel": "已安装",
|
||||
"uninstalledLabel": "未安装",
|
||||
"version": "版本",
|
||||
"title": "名称",
|
||||
"desc": "简介",
|
||||
"plugDetail": "插件信息",
|
||||
"author": "作者",
|
||||
"detail":"详情",
|
||||
"detail": "详情",
|
||||
"addonInstall": "插件安装",
|
||||
"dirPermission": "目录读写权限",
|
||||
"path": "路径",
|
||||
@ -28,23 +28,24 @@
|
||||
"envCheck": "环境检查",
|
||||
"installProgress": "安装进度",
|
||||
"installComplete": "安装完成",
|
||||
"localAppText":"插件管理",
|
||||
"marketAppText":"官方市场",
|
||||
"localAppText": "插件管理",
|
||||
"marketAppText": "官方市场",
|
||||
"installShowDialogCloseTips": "安装任务尚未完成,关闭将取消安装任务,是否要继续关闭?",
|
||||
"marketDevelopMessage":"官方市场正在开发中!",
|
||||
"marketDevelopMessage": "官方市场正在开发中!",
|
||||
"jobError": "任务队列未启动 请在服务端源码部署目录打开终端执行 php think queue:listen",
|
||||
"conflictFiles": "冲突文件",
|
||||
"process": "启动进程",
|
||||
"open": "开启",
|
||||
"down": "下载",
|
||||
"installDown":"未安装(重新下载)",
|
||||
"unloadDown":"已安装(重新下载)",
|
||||
"installDown": "未安装(重新下载)",
|
||||
"unloadDown": "已安装(重新下载)",
|
||||
"addonVersion": "插件版本",
|
||||
"versionCode": "版本号",
|
||||
"createTime": "发布时间",
|
||||
"buyLabel": "已购买",
|
||||
"recentlyUpdated": "最近更新",
|
||||
"installTips": "安装后需手动更新插件引用的依赖和编译各个端口的前端源码",
|
||||
"localInstall":"本地安装",
|
||||
"localInstall": "本地安装",
|
||||
"cloudInstall": "一键云安装",
|
||||
"cloudInstallTips": "云安装可实现一键安装,安装后无需手动更新依赖和编译前端源码",
|
||||
"installingTips": "有插件正在安装中请等待安装完成之后再进行其他操作,点击查看",
|
||||
@ -58,6 +59,7 @@
|
||||
"link": "官方应用市场",
|
||||
"descriptionRight": "逛逛",
|
||||
"installed-empty": "暂未安装任何应用",
|
||||
"recentlyUpdatedEmpty": "暂无最近更新应用",
|
||||
"siteAddressTips": "授权域名不匹配",
|
||||
"authCodePlaceholder": "请输入授权码",
|
||||
"authSecretPlaceholder": "请输入授权秘钥",
|
||||
@ -65,12 +67,13 @@
|
||||
"notHaveAuth": "还没有授权?去购买",
|
||||
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
|
||||
"addonUninstall": "插件卸载",
|
||||
"appIdentification":"应用标识",
|
||||
"tipText":"标识指开发应用或插件的文件夹名称",
|
||||
"appIdentification": "应用标识",
|
||||
"tipText": "标识指开发应用或插件的文件夹名称",
|
||||
"uninstallTips": "是否要卸载该插件?",
|
||||
"upgrade": "升级",
|
||||
"upgrade": "一键升级",
|
||||
"newVersion": "最新版本",
|
||||
"cloudBuild": "云编译",
|
||||
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?",
|
||||
"deleteAddonTips": "删除插件会把插件目录连同文件全部删除,确定要删除吗?"
|
||||
"deleteAddonTips": "删除插件会把插件目录连同文件全部删除,确定要删除吗?",
|
||||
"batchUpgrade": "批量升级"
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
"logging": "登录中",
|
||||
"platform": "管理端",
|
||||
"login" : "登录",
|
||||
"siteLogin": "站点登录",
|
||||
"adminLogin": "平台登录",
|
||||
"userPlaceholder": "请输入您的账号",
|
||||
"passwordPlaceholder": "请输入您的密码",
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
"continueSign": "连签天数",
|
||||
"continueSignFormatError": "连签天数格式错误",
|
||||
"continueSignBerweenDays": "连签天数为2-365天",
|
||||
"continueSignMustLessThanSignPeriod": "连签天数不能大于签到周期",
|
||||
"receiveLimit": "领取限制",
|
||||
"noLimit": "不限制",
|
||||
"everyOneLimit": "每人限领",
|
||||
|
||||
@ -15,17 +15,17 @@
|
||||
"searchValueEmptyTips": "请输入搜索内容",
|
||||
"verify": "核销",
|
||||
"buyInfo": "预订信息",
|
||||
"orderRefunding": "该订单正在维权中不能进行核销",
|
||||
"orderRefunding": "该订单正在售后中不能进行核销",
|
||||
"verifyTips": "是否要核销该订单?",
|
||||
"toOrder": "查看订单",
|
||||
"verifyType": "核销类型",
|
||||
"verifyTypePlaceholder": "请选择核销类型",
|
||||
"verifier": "核销员",
|
||||
"createTime":"添加时间",
|
||||
"addVerifier":"添加核销员",
|
||||
"verifierDeleteTips":"确定要删除该核销员吗?",
|
||||
"createTime": "添加时间",
|
||||
"addVerifier": "添加核销员",
|
||||
"verifierDeleteTips": "确定要删除该核销员吗?",
|
||||
"memberInfo": "会员信息",
|
||||
"memberIdPlaceholder": "请选择会员",
|
||||
"member": "会员",
|
||||
"searchPlaceholder": "请输入会员昵称搜索"
|
||||
"searchPlaceholder": "请输入会员编号/昵称/手机号"
|
||||
}
|
||||
@ -15,16 +15,16 @@
|
||||
"searchValueEmptyTips": "请输入搜索内容",
|
||||
"verify": "核销",
|
||||
"buyInfo": "预订信息",
|
||||
"orderRefunding": "该订单正在维权中不能进行核销",
|
||||
"orderRefunding": "该订单正在售后中不能进行核销",
|
||||
"verifyTips": "是否要核销该订单?",
|
||||
"toOrder": "查看订单",
|
||||
"verifyType": "核销类型",
|
||||
"verifyTypePlaceholder": "请选择核销类型",
|
||||
"verifier": "核销员",
|
||||
"verifierPlaceholder": "请选择核销员",
|
||||
"createTime":"添加时间",
|
||||
"addVerifier":"添加核销员",
|
||||
"verifierDeleteTips":"确定要删除该核销员吗?",
|
||||
"createTime": "添加时间",
|
||||
"addVerifier": "添加核销员",
|
||||
"verifierDeleteTips": "确定要删除该核销员吗?",
|
||||
"memberInfo": "会员信息",
|
||||
"memberIdPlaceholder": "请选择会员",
|
||||
"member": "会员",
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
"type": "协议类型",
|
||||
"titlePlaceholder": "请输入协议标题",
|
||||
"contentPlaceholder": "请填写协议内容",
|
||||
"contentMaxTips": "协议内容字符数应在5~100000之间",
|
||||
"contentMaxTips": "协议内容字符数应在5~10000之间",
|
||||
"content": "内容"
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"websiteInfo":"后台设置",
|
||||
"contactAddress":"联系地址",
|
||||
"websiteInfo": "后台设置",
|
||||
"contactAddress": "联系地址",
|
||||
"siteName": "站点名称",
|
||||
"keywords": "网站关键字",
|
||||
"logo": "长方形Logo",
|
||||
@ -12,7 +12,7 @@
|
||||
"address": "详细地址",
|
||||
"phone": "客服电话",
|
||||
"businessHours": "营业时间",
|
||||
"contactAddressPlaceholder":"联系地址",
|
||||
"contactAddressPlaceholder": "联系地址",
|
||||
"siteNamePlaceholder": "站点名称",
|
||||
"keywordsPlaceholder": "网站关键字",
|
||||
"descPlaceholder": "网站简介",
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
"appPublicCertPathTips": "上传appCertPublicKey文件",
|
||||
"alipayPublicCertPathTips": "上传alipayCertPublicKey文件",
|
||||
"alipayRootCertPathTips": "上传alipayRootCert文件",
|
||||
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景"
|
||||
|
||||
"operationTip": "温馨提示:打款设置用于会员提现转账,发放红包等场景",
|
||||
"transferTips":"注意:应微信方规定,在2025年1月15日前开通商家转账到零钱服务的商户号可正常使用转账功能,之后开通的不支持使用转账到零钱服务",
|
||||
"wechatpayPublicCert": "微信支付公钥",
|
||||
"wechatpayPublicCertId": "微信支付公钥ID"
|
||||
}
|
||||
|
||||
28
admin/src/app/lang/zh-cn/tools.backup_records.json
Normal file
28
admin/src/app/lang/zh-cn/tools.backup_records.json
Normal 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": "无"
|
||||
}
|
||||
12
admin/src/app/lang/zh-cn/tools.upgrade_records.json
Normal file
12
admin/src/app/lang/zh-cn/tools.upgrade_records.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "编号",
|
||||
"upgradeName": "内容",
|
||||
"prevVersion": "前一版本",
|
||||
"currentVersion": "版本",
|
||||
"upgradeNamePlaceholder": "请输入内容",
|
||||
"completeTime": "升级时间",
|
||||
"status": "状态",
|
||||
"failReason": "失败原因",
|
||||
"batchDelete": "批量删除",
|
||||
"deleteTips": "确定要删除吗?"
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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 {
|
||||
|
||||
195
admin/src/app/views/app/upgrade.vue
Normal file
195
admin/src/app/views/app/upgrade.vue
Normal 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>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<!--站点菜单-->
|
||||
<!--平台菜单-->
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
@ -94,38 +96,38 @@
|
||||
</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({
|
||||
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({
|
||||
const attachment: Record<string, any> = reactive({
|
||||
loading: true,
|
||||
page: 1,
|
||||
total: 0,
|
||||
limit: 10,
|
||||
data: []
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
/**
|
||||
* 查询素材
|
||||
*/
|
||||
const getAttachmentList = (page: number = 1) => {
|
||||
const getAttachmentList = (page: number = 1) => {
|
||||
attachment.loading = true
|
||||
attachment.page = page
|
||||
getMediaList({
|
||||
@ -140,18 +142,18 @@
|
||||
}).catch(() => {
|
||||
attachment.loading = false
|
||||
})
|
||||
}
|
||||
getAttachmentList()
|
||||
}
|
||||
getAttachmentList()
|
||||
|
||||
const emits = defineEmits(['success'])
|
||||
const selectedFile: Record<string, any> = ref({})
|
||||
const emits = defineEmits(['success'])
|
||||
const selectedFile: Record<string, any> = ref({})
|
||||
|
||||
const confirm = () => {
|
||||
const confirm = () => {
|
||||
emits('success', selectedFile.value)
|
||||
}
|
||||
}
|
||||
|
||||
const syncLoading = ref(false)
|
||||
const syncWechatNews = () => {
|
||||
const syncLoading = ref(false)
|
||||
const syncWechatNews = () => {
|
||||
if (syncLoading.value) return
|
||||
syncLoading.value = true
|
||||
|
||||
@ -161,18 +163,18 @@
|
||||
}).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(() => {
|
||||
// 瀑布流计算
|
||||
const waterfallContainerRef = ref(null)
|
||||
const waterfallItemRef = ref([])
|
||||
const listPosition = ref([])
|
||||
const waterfall = debounce(() => {
|
||||
nextTick(() => {
|
||||
const containerWidth = waterfallContainerRef.value.clientWidth
|
||||
const column = parseInt(containerWidth / 292)
|
||||
@ -202,10 +204,10 @@
|
||||
})
|
||||
listPosition.value = positions
|
||||
})
|
||||
}, 800)
|
||||
}, 800)
|
||||
|
||||
// 重新布局,以适应窗口变化
|
||||
window.addEventListener('resize', () => waterfall())
|
||||
// 重新布局,以适应窗口变化
|
||||
window.addEventListener('resize', () => waterfall())
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,12 +69,23 @@ const open = (option:any) => {
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
title: [
|
||||
{ required: true, message: "请输入颜色名称", trigger: 'blur' }
|
||||
],
|
||||
value: [
|
||||
{ required: true, message: "请输入颜色value值", trigger: 'blur' }
|
||||
{
|
||||
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' },
|
||||
@ -93,14 +103,16 @@ const formRules = reactive<FormRules>({
|
||||
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;
|
||||
}
|
||||
|
||||
@ -15,16 +15,16 @@
|
||||
<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>
|
||||
|
||||
@ -32,7 +32,9 @@
|
||||
|
||||
<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]">
|
||||
<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>
|
||||
@ -53,7 +55,8 @@
|
||||
<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>
|
||||
<span class="text-primary flex-1 cursor-pointer"
|
||||
@click="showBlockStyle">{{ diyStore.editComponent.blockStyle.title }}</span>
|
||||
<el-icon>
|
||||
<ArrowRight />
|
||||
</el-icon>
|
||||
@ -62,7 +65,9 @@
|
||||
<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]">
|
||||
<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>
|
||||
@ -81,13 +86,15 @@
|
||||
<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]">
|
||||
<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"/>
|
||||
<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-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'">
|
||||
@ -95,7 +102,7 @@
|
||||
</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-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'">
|
||||
@ -104,40 +111,45 @@
|
||||
</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"/>
|
||||
<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 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>
|
||||
@ -151,7 +163,7 @@
|
||||
<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-color-picker v-model="diyStore.editComponent.titleColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -160,12 +172,13 @@
|
||||
<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-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-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>
|
||||
@ -175,14 +188,14 @@
|
||||
<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 :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 :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')">
|
||||
@ -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')
|
||||
@ -265,25 +278,25 @@ const showTitleStyle = () => {
|
||||
|
||||
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'
|
||||
}
|
||||
])
|
||||
|
||||
@ -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: '' }
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<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-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">
|
||||
@ -43,11 +43,11 @@
|
||||
<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"/>
|
||||
<diy-link v-model="diyStore.editComponent.search.link" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@ -76,20 +76,21 @@
|
||||
<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-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>
|
||||
|
||||
<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 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-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"/>
|
||||
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -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,8 +155,10 @@
|
||||
<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"
|
||||
<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]">
|
||||
@ -169,28 +176,33 @@
|
||||
<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>
|
||||
@ -204,7 +216,7 @@
|
||||
<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-color-picker v-model="diyStore.editComponent.search.positionColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -213,12 +225,12 @@
|
||||
<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-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-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>
|
||||
@ -227,16 +239,16 @@
|
||||
<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-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-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-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-color-picker v-model="diyStore.editComponent.search.btnBgColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -245,16 +257,16 @@
|
||||
<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-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-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-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-color-picker v-model="diyStore.editComponent.tab.fixedSelectColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -270,10 +282,12 @@
|
||||
</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-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-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>
|
||||
@ -296,10 +310,10 @@
|
||||
</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-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-color-picker v-model="diyStore.editComponent.swiper.indicatorActiveColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -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{
|
||||
.select-diy-page-input .el-input__inner {
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapse-wrap{
|
||||
.el-collapse-item__header{
|
||||
}
|
||||
|
||||
.collapse-wrap {
|
||||
.el-collapse-item__header {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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) // 数据是否发生变化,true:没变化,false:变化了
|
||||
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
|
||||
|
||||
@ -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({})
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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">
|
||||
<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')
|
||||
@ -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]))
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"
|
||||
@ -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};
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -2,9 +2,8 @@
|
||||
<!--应用市场-->
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{ t('localAppText') }}</span>
|
||||
<span class="text-page-title">{{ t("localAppText") }}</span>
|
||||
|
||||
<el-input class="!w-[250px]" :placeholder="t('search')" v-model.trim="search_name" @keyup.enter="query">
|
||||
<template #suffix>
|
||||
@ -15,48 +14,53 @@
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between my-[20px]">
|
||||
<div class="flex justify-between items-center my-[20px]">
|
||||
<div class="flex">
|
||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'installed' }]" @click="activeNameTabFn('installed')">
|
||||
{{ t('installLabel') }}
|
||||
</div>
|
||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'uninstalled' }]" @click="activeNameTabFn('uninstalled')">
|
||||
{{ t('uninstalledLabel') }}
|
||||
</div>
|
||||
<div :class="['flex items-center text-[14px] h-[32px] text-[#a6a9ad] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'all' }]" @click="activeNameTabFn('all')">
|
||||
{{ t('buyLabel') }}
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'installed' }]" @click="activeNameTabFn('installed')">{{ t("installLabel") }}</div>
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'uninstalled' }]" @click="activeNameTabFn('uninstalled')">{{ t("uninstalledLabel") }}</div>
|
||||
<div :class="['flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'all' }]" @click="activeNameTabFn('all')">{{ t("buyLabel") }}</div>
|
||||
<div :class="['relative flex items-center text-[14px] h-[32px] border-[1px] border-solid my-[3px] border-[var(--el-color-info-light-8)] rounded-full px-[20px] mr-[24px] cursor-pointer hover:bg-[var(--el-color-info-light-8)]', { '!text-[#fff] !bg-[#000] !border-[#000]': activeName === 'recentlyUpdated' }]" @click="activeNameTabFn('recentlyUpdated')">
|
||||
<span v-if="localList['recentlyUpdated'].length > 0" class="w-[9px] h-[9px] bg-[#FF0000]" style="position: absolute; border-radius: 50%; right: 5px; top: -5px"></span>
|
||||
<span>{{ t('recentlyUpdated') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" round @click="handleCloudBuild" :loading="cloudBuildRef?.loading">{{ t('cloudBuild') }}</el-button>
|
||||
<div>
|
||||
<el-button type="primary" v-show="activeName === 'recentlyUpdated'" round @click="batchUpgrade" :loading="upgradeRef?.loading" :disabled="authLoading">{{ t("batchUpgrade") }}</el-button>
|
||||
<el-button type="primary" round @click="handleCloudBuild" :loading="cloudBuildRef?.loading" :disabled="authLoading">{{ t("cloudBuild") }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-table v-if="localList[activeName].length&&!authLoading" :data="info[activeName]" size="large" class="pt-[5px]">
|
||||
<el-table-column :label="t('appName')" align="left" width="320">
|
||||
<el-table v-if="localList[activeName].length && !loading" :data="info[activeName]" size="large" class="pt-[5px]" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" v-if="activeName === 'recentlyUpdated'" />
|
||||
<el-table-column :label="t('appName')" align="left" width="450">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center cursor-pointer" @click = "handleTips">
|
||||
<div class="flex items-center cursor-pointer">
|
||||
<el-image class="w-[54px] h-[54px]" :src="row.icon" fit="contain">
|
||||
<template #error>
|
||||
<div class="flex items-center w-full h-full">
|
||||
<img class="max-w-full max-h-full" src="@/app/assets/images/icon-addon.png" alt="">
|
||||
<img class="max-w-full max-h-full" src="@/app/assets/images/icon-addon.png" alt="" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="flex flex-col justify-center pl-[20px] font-500 text-[13px]">
|
||||
<div class="flex-1 w-0 flex flex-col justify-center pl-[20px] font-500 text-[13px]">
|
||||
<div class="w-[236px] truncate leading-[18px]">{{ row.title }}</div>
|
||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-if="row.install_info && Object.keys(row.install_info)?.length">{{ row.install_info.version }}</div>
|
||||
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-else>{{ row.version }}</div>
|
||||
<div class="mt-[3px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">
|
||||
<el-tag type="danger" size="small">{{ t('newVersion') }}{{ row.version }}</el-tag>
|
||||
<div class="mt-[3px] flex flex-nowrap">
|
||||
<el-tag type="danger" size="small" v-if="activeName == 'recentlyUpdated' && row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">{{ t("newVersion") }}{{ row.version }}</el-tag>
|
||||
<el-tooltip v-if="versionJudge(row)" effect="dark" content="该插件与框架版本不兼容,可能存在未知问题" placement="top-start">
|
||||
<el-tag type="info" size="small" class="ml-[3px]">该插件与框架版本不兼容,可能存在未知问题</el-tag>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" min-width="120">
|
||||
<el-table-column align="left" min-width="150">
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<span class="font-500 text-[13px] mr-[5px]">{{ t('appIdentification') }}</span>
|
||||
<span class="font-500 text-[13px] mr-[5px]">{{ t("appIdentification") }}</span>
|
||||
<el-tooltip class="box-item" effect="light" :content="t('tipText')" placement="bottom">
|
||||
<el-icon class="cursor-pointer text-[16px] text-[#a9a9a9]">
|
||||
<QuestionFilled />
|
||||
@ -73,76 +77,77 @@
|
||||
<span class="font-500 text-[13px] multi-hidden">{{ row.desc }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('type')" align="left" min-width="100">
|
||||
<el-table-column :label="t('type')" align="left" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<span class="font-500 text-[13px]">{{ row.type === 'app' ? t('app') : t('addon') }}</span>
|
||||
<span class="font-500 text-[13px]">{{ row.type === "app" ? t("app") : t("addon") }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="" :label="t('author')" align="left" min-width="100">
|
||||
<el-table-column prop="" :label="t('author')" align="left" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<span class="font-500 text-[13px]">{{ row.author }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="200">
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="250">
|
||||
<template #default="{ row }">
|
||||
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version" type="primary" link @click="upgradeAddonFn(row.key)">{{ t('upgrade') }}</el-button>
|
||||
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length" type="primary" link @click="uninstallAddonFn(row.key)">{{ t('unload') }}</el-button>
|
||||
<el-button class="!text-[13px]" v-if="activeName == 'recentlyUpdated' && row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version" type="primary" link @click="upgradeAddonFn(row.key)">{{ t("upgrade") }}</el-button>
|
||||
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length" type="primary" link @click="uninstallAddonFn(row.key)">{{ t("unload") }}</el-button>
|
||||
<template v-if="row.is_download && (!row.install_info || !Object.keys(row.install_info).length)">
|
||||
<el-button class="!text-[13px]" type="primary" link @click="installAddonFn(row.key)">{{ t('install') }}</el-button>
|
||||
<el-button class="!text-[13px]" type="primary" link @click="deleteAddonFn(row.key)">{{ t('delete') }}</el-button>
|
||||
<el-button class="!text-[13px]" type="primary" link @click="installAddonFn(row.key)">{{ t("install") }}</el-button>
|
||||
<el-button class="!text-[13px]" type="primary" link @click="deleteAddonFn(row.key)">{{ t("delete") }}</el-button>
|
||||
</template>
|
||||
<el-button class="!text-[13px]" v-if="!row.is_download" :loading="downloading == row.key" :disabled="downloading != ''" type="primary" link @click.stop="downEvent(row)">
|
||||
<span>{{ t('down') }}</span>
|
||||
<span>{{ t("down") }}</span>
|
||||
</el-button>
|
||||
<el-button class="!text-[13px]" type="primary" link @click="getAddonDetialFn(row)">{{ t('detail') }}</el-button>
|
||||
<el-button class="!text-[13px]" type="primary" link @click="getAddonDetailFn(row)">{{ t("detail") }}</el-button>
|
||||
<el-button class="!text-[13px]" type="primary" link @click="updateInformationFn(row)">更新信息</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="data-loading" v-if="authLoading || !localList[activeName].length">
|
||||
<div class="data-loading" v-if="loading || !localList[activeName].length">
|
||||
<el-table :data="[]" size="large" class="pt-[5px]">
|
||||
<el-table-column :label="t('appName')" align="left" width="320"></el-table-column>
|
||||
<el-table-column align="left" min-width="120"></el-table-column>
|
||||
<el-table-column prop="" :label="t('introduction')" align="left" min-width="200"></el-table-column>
|
||||
<el-table-column :label="t('type')" align="left" min-width="100"></el-table-column>
|
||||
<el-table-column prop="" :label="t('author')" align="left" min-width="100"></el-table-column>
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="150"></el-table-column>
|
||||
<template #empty><span></span></template>
|
||||
<el-table-column :label="t('appName')" align="left" width="320" />
|
||||
<el-table-column align="left" min-width="120" />
|
||||
<el-table-column :label="t('introduction')" align="left" min-width="200" />
|
||||
<el-table-column :label="t('type')" align="left" min-width="100" />
|
||||
<el-table-column :label="t('author')" align="left" min-width="100" />
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="150" />
|
||||
<template #empty>
|
||||
<span></span>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="h-[100px]" v-loading="authLoading" v-if="authLoading">
|
||||
<div class="h-[100px]" v-loading="loading" v-if="loading"></div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading && activeName == 'installed'&&!authLoading">
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.installed.length && !loading && activeName == 'installed' && !authLoading">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">{{ t('installed-empty') }}</p>
|
||||
<p class="flex items-center">{{ t("installed-empty") }}</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled'&&!authLoading">
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.uninstalled.length && !loading && activeName == 'uninstalled' && !authLoading">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">
|
||||
<span>{{ t('descriptionLeft') }}</span>
|
||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
|
||||
<span>{{ t('descriptionRight') }}</span>
|
||||
<span>{{ t("descriptionLeft") }}</span>
|
||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t("link") }}</el-link>
|
||||
<span>{{ t("descriptionRight") }}</span>
|
||||
</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<div v-if="!localList.all.length && !loading && !authinfo && activeName == 'all'&&!authLoading" class="mx-auto overview-empty flex flex-col items-center pt-14 pb-6">
|
||||
<div v-if="!localList.all.length && !loading && !authinfo && activeName == 'all' && !authLoading" class="mx-auto overview-empty flex flex-col items-center pt-14 pb-6">
|
||||
<div class="mb-[20px] text-sm text-[#888]">检测到当前账号尚未绑定授权,请先绑定授权!</div>
|
||||
<div class="flex flex-1 flex-wrap justify-center relative">
|
||||
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary" @click="authCodeApproveFn">授权码认证</el-button>
|
||||
<el-popover ref="getAuthCodeDialog" placement="bottom" :width="478" trigger="click" class="mt-[8px]">
|
||||
<div class="px-[18px] py-[8px]">
|
||||
<p class="leading-[32px] text-[14px]">
|
||||
您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
||||
<p class="leading-[32px] text-[14px]">您在官方应用市场购买任意一款应用,即可获得授权码。输入正确授权码认证通过后,即可支持在线升级和其它相关服务</p>
|
||||
<div class="flex justify-end mt-[36px]">
|
||||
<el-button class="w-[182px] !h-[48px]" plain @click="market">去应用市场逛逛</el-button>
|
||||
<el-button class="w-[100px] !h-[48px]" plain @click="getAuthCodeDialog.hide()">关闭</el-button>
|
||||
@ -154,20 +159,30 @@
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.all.length && !loading && authinfo && activeName == 'all'&&!authLoading">
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.all.length && !loading && authinfo && activeName == 'all' && !authLoading">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">
|
||||
<span>{{ t('buyDescriptionLeft') }}</span>
|
||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t('link') }}</el-link>
|
||||
<span>{{ t('descriptionRight') }}</span>
|
||||
<span>{{ t("buyDescriptionLeft") }}</span>
|
||||
<el-link type="primary" @click="goRouter" class="mx-[5px]">{{ t("link") }}</el-link>
|
||||
<span>{{ t("descriptionRight") }}</span>
|
||||
</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
<el-empty class="mx-auto overview-empty" v-if="!localList.recentlyUpdated.length && !loading && authinfo && activeName == 'recentlyUpdated' && !authLoading">
|
||||
<template #image>
|
||||
<div class="w-[230px] mx-auto">
|
||||
<img src="@/app/assets/images/index/apply_empty.png" class="max-w-full" alt="" />
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<p class="flex items-center">{{ t("recentlyUpdatedEmpty") }}</p>
|
||||
</template>
|
||||
</el-empty>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="authCodeApproveDialog" title="授权码认证" width="400px">
|
||||
@ -183,13 +198,14 @@
|
||||
</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>
|
||||
@ -198,21 +214,21 @@
|
||||
<el-dialog v-model="appStoreShowDialog" :title="t('plugDetail')" width="500px" :destroy-on-close="true">
|
||||
<el-form :model="appStoreInfo" label-width="120px" ref="formRef" class="page-form">
|
||||
<el-form-item :label="t('title')">
|
||||
<div class="input-width"> {{ appStoreInfo.title }} </div>
|
||||
<div class="input-width">{{ appStoreInfo.title }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('desc')">
|
||||
<div class="input-width"> {{ appStoreInfo.desc }} </div>
|
||||
<div class="input-width">{{ appStoreInfo.desc }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('author')">
|
||||
<div class="input-width"> {{ appStoreInfo.author }} </div>
|
||||
<div class="input-width">{{ appStoreInfo.author }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('version')">
|
||||
<div class="input-width"> {{ appStoreInfo.version }} </div>
|
||||
<div class="input-width">{{ appStoreInfo.version }}</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="appStoreShowDialog = false">{{ t('confirm') }}</el-button>
|
||||
<el-button type="primary" @click="appStoreShowDialog = false">{{ t("confirm") }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@ -228,28 +244,32 @@
|
||||
<el-scrollbar max-height="50vh">
|
||||
<div class="min-h-[150px]">
|
||||
<div class="my-3" v-if="installCheckResult.dir">
|
||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
||||
<p class="pt-[20px] pl-[20px]">{{ t("dirPermission") }}</p>
|
||||
<div class="px-[20px] pt-[10px] text-[14px]">
|
||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||
<el-col :span="12">
|
||||
<span>{{ t('path') }}</span>
|
||||
<span>{{ t("path") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('demand') }}</span>
|
||||
<span>{{ t("demand") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('status') }}</span>
|
||||
<span>{{ t("status") }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item,index) in installCheckResult.dir.is_readable" :key="index">
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in installCheckResult.dir.is_readable" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('readable') }}</span>
|
||||
<span>{{ t("readable") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
||||
<span v-if="item.status">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
@ -257,15 +277,19 @@
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item,index) in installCheckResult.dir.is_write" :key="index">
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in installCheckResult.dir.is_write" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('write') }}</span>
|
||||
<span>{{ t("write") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
||||
<span v-if="item.status">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
@ -279,20 +303,20 @@
|
||||
</el-scrollbar>
|
||||
<div class="flex justify-end">
|
||||
<el-tooltip effect="dark" :content="t('installTips')" placement="top">
|
||||
<el-button type="default" :disabled="!installCheckResult.is_pass || cloudInstalling" :loading="localInstalling" @click="handleInstall">{{ t('localInstall') }}</el-button>
|
||||
<el-button :disabled="!installCheckResult.is_pass || cloudInstalling" :loading="localInstalling" @click="handleInstall">{{ t("localInstall") }}</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" :content="t('cloudInstallTips')" placement="top">
|
||||
<el-button type="primary" :disabled="!installCheckResult.is_pass || localInstalling" :loading="cloudInstalling" @click="handleCloudInstall">{{ t('cloudInstall') }}</el-button>
|
||||
<el-button type="primary" :disabled="!installCheckResult.is_pass || localInstalling" :loading="cloudInstalling" @click="handleCloudInstall">{{ t("cloudInstall") }}</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="installStep == 2" class="h-[50vh] mt-[20px]">
|
||||
<terminal ref="terminalRef" :context="currAddon" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd"/>
|
||||
<terminal ref="terminalRef" :context="currAddon" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd" />
|
||||
</div>
|
||||
<div v-show="installStep == 3" class="h-[50vh] mt-[20px] flex flex-col">
|
||||
<el-result icon="success" :title="t('addonInstallSuccess')"></el-result>
|
||||
<!-- 提示信息 -->
|
||||
<div v-for="(item,index) in installAfterTips" class="mb-[10px]" :key="index">
|
||||
<div v-for="(item, index) in installAfterTips" class="mb-[10px]" :key="index">
|
||||
<el-alert :title="item" type="error" :closable="false" />
|
||||
</div>
|
||||
</div>
|
||||
@ -302,28 +326,32 @@
|
||||
<el-scrollbar max-height="50vh">
|
||||
<div class="min-h-[150px]">
|
||||
<div class="bg-[#fff] my-3" v-if="uninstallCheckResult.dir">
|
||||
<p class="pt-[20px] pl-[20px] ">{{ t('dirPermission') }}</p>
|
||||
<p class="pt-[20px] pl-[20px]">{{ t("dirPermission") }}</p>
|
||||
<div class="px-[20px] pt-[10px] text-[14px]">
|
||||
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
|
||||
<el-col :span="12">
|
||||
<span>{{ t('path') }}</span>
|
||||
<span>{{ t("path") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('demand') }}</span>
|
||||
<span>{{ t("demand") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('status') }}</span>
|
||||
<span>{{ t("status") }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item,index) in uninstallCheckResult.dir.is_readable" :key="index">
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in uninstallCheckResult.dir.is_readable" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('readable') }}</span>
|
||||
<span>{{ t("readable") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
||||
<span v-if="item.status">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
@ -331,15 +359,19 @@
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item,index) in uninstallCheckResult.dir.is_write" :key="index">
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="(item, index) in uninstallCheckResult.dir.is_write" :key="index">
|
||||
<el-col :span="12">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span>{{ t('write') }}</span>
|
||||
<span>{{ t("write") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
|
||||
<span v-if="item.status">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
@ -363,20 +395,31 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 更新信息 -->
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<upgrade ref="upgradeRef" @complete="localListFn"/>
|
||||
<upgrade-log :upgradeKey="upgradeKey" ref="upgradeLogRef" />
|
||||
<upgrade ref="upgradeRef" @complete="localListFn" @cloudbuild="handleCloudBuild" />
|
||||
<cloud-build ref="cloudBuildRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch, h } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, cloudInstallAddon, getAddonInstalltask, getAddonCloudInstallLog, preUninstallCheck, cancelInstall } from '@/app/api/addon'
|
||||
import {
|
||||
getAddonLocal,
|
||||
uninstallAddon,
|
||||
installAddon,
|
||||
preInstallCheck,
|
||||
cloudInstallAddon,
|
||||
getAddonInstalltask,
|
||||
getAddonCloudInstallLog,
|
||||
preUninstallCheck,
|
||||
cancelInstall
|
||||
} from '@/app/api/addon'
|
||||
import { deleteAddonDevelop } from '@/app/api/tools'
|
||||
import { getInstallConfig } from '@/app/api/sys'
|
||||
import { downloadVersion, getAuthInfo, setAuthInfo } from '@/app/api/module'
|
||||
import { getVersions } from '@/app/api/auth'
|
||||
import { ElMessage, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus'
|
||||
import 'vue-web-terminal/lib/theme/dark.css'
|
||||
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
||||
@ -386,6 +429,7 @@ import { useRouter, useRoute } from 'vue-router'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
import Upgrade from '@/app/components/upgrade/index.vue'
|
||||
import CloudBuild from '@/app/components/cloud-build/index.vue'
|
||||
import UpgradeLog from '@/app/components/upgrade-log/index.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -398,18 +442,18 @@ const installAfterTips = ref<string[]>([])
|
||||
const userStore = useUserStore()
|
||||
const unloadHintDialog = ref(false)
|
||||
const terminalRef = ref(null)
|
||||
const installPhpConfig = ref(null)
|
||||
|
||||
getInstallConfig().then(({ data }) => {
|
||||
installPhpConfig.value = data
|
||||
}).catch()
|
||||
const frameworkVersion = ref('')
|
||||
const upgradeLogRef = ref<any>(null)
|
||||
getVersions().then((res) => {
|
||||
frameworkVersion.value = res.data.version.version
|
||||
})
|
||||
|
||||
const currDownData = ref()
|
||||
const downEventHintFn = () => {
|
||||
downEvent(currDownData.value, true)
|
||||
}
|
||||
|
||||
const activeNameTabFn = (data:any) => {
|
||||
const activeNameTabFn = (data: any) => {
|
||||
activeName.value = data
|
||||
storage.set({ key: 'storeActiveName', data })
|
||||
}
|
||||
@ -437,11 +481,10 @@ const downEvent = (param: Record<string, any>, isDown = false) => {
|
||||
}
|
||||
|
||||
const authCode = ref('')
|
||||
getAuthInfo().then(res => {
|
||||
getAuthInfo().then((res) => {
|
||||
if (res.data.data && res.data.data.auth_code) {
|
||||
authCode.value = res.data.data.auth_code
|
||||
}
|
||||
}).catch(() => {
|
||||
})
|
||||
|
||||
/**
|
||||
@ -452,39 +495,47 @@ const search_name = ref('')
|
||||
const info = ref({
|
||||
installed: [],
|
||||
uninstalled: [],
|
||||
all: []
|
||||
all: [],
|
||||
recentlyUpdated: []
|
||||
})
|
||||
const query = () => {
|
||||
if (search_name.value == '' || search_name.value == null) {
|
||||
info.value.installed = localList.value.installed
|
||||
info.value.uninstalled = localList.value.uninstalled
|
||||
info.value.all = localList.value.all
|
||||
info.value.recentlyUpdated = localList.value.recentlyUpdated
|
||||
return false
|
||||
}
|
||||
info.value.installed = localList.value.installed.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||
info.value.uninstalled = localList.value.uninstalled.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||
info.value.all = localList.value.all.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||
info.value.recentlyUpdated = localList.value.recentlyUpdated.filter((el: any) => el.title.indexOf(search_name.value) != -1)
|
||||
}
|
||||
const localList = ref({
|
||||
installed: [],
|
||||
uninstalled: [],
|
||||
all: [],
|
||||
recentlyUpdated: [],
|
||||
error: ''
|
||||
})
|
||||
|
||||
const localListFn = () => {
|
||||
loading.value = true
|
||||
getAddonLocal({}).then(res => {
|
||||
getAddonLocal({}).then((res) => {
|
||||
const data = res.data.list
|
||||
localList.value.error = res.data.error
|
||||
localList.value.installed = []
|
||||
localList.value.uninstalled = []
|
||||
localList.value.all = []
|
||||
localList.value.recentlyUpdated = []
|
||||
for (const i in data) {
|
||||
if (data[i].is_local == false) localList.value.all.push(data[i])
|
||||
|
||||
if (data[i].install_info && Object.keys(data[i].install_info)?.length) {
|
||||
localList.value.installed.push(data[i])
|
||||
if (data[i].install_info.version != data[i].version) {
|
||||
localList.value.recentlyUpdated.push(data[i])
|
||||
}
|
||||
} else {
|
||||
if (data[i].is_download == true) localList.value.uninstalled.push(data[i])
|
||||
}
|
||||
@ -506,11 +557,6 @@ const localListFn = () => {
|
||||
|
||||
localListFn()
|
||||
|
||||
// 点击应用提示到站点
|
||||
const handleTips = () => {
|
||||
ElMessage('请在站点中运行程序!')
|
||||
}
|
||||
|
||||
// 点击应用可以进系统
|
||||
|
||||
const appLink: any = ref({})
|
||||
@ -539,7 +585,7 @@ const installCheckResult = ref({})
|
||||
|
||||
let flashInterval = null
|
||||
const terminalFlash = new TerminalFlash()
|
||||
const onExecCmd = (key, command, success, failed, name)=> {
|
||||
const onExecCmd = (key, command, success, failed, name) => {
|
||||
if (command == '开始安装插件') {
|
||||
success(terminalFlash)
|
||||
const frames = makeIterator(['/', '——', '\\', '|'])
|
||||
@ -549,11 +595,11 @@ const onExecCmd = (key, command, success, failed, name)=> {
|
||||
}
|
||||
}
|
||||
|
||||
function makeIterator(array: string[]) {
|
||||
function makeIterator (array: string[]) {
|
||||
let nextIndex = 0
|
||||
return {
|
||||
next() {
|
||||
if ((nextIndex + 1) == array.length) {
|
||||
next () {
|
||||
if (nextIndex + 1 == array.length) {
|
||||
nextIndex = 0
|
||||
}
|
||||
return { value: array[nextIndex++] }
|
||||
@ -568,13 +614,13 @@ function makeIterator(array: string[]) {
|
||||
const installAddonFn = (key: string) => {
|
||||
currAddon.value = key
|
||||
|
||||
preInstallCheck(key).then(res => {
|
||||
preInstallCheck(key).then((res) => {
|
||||
installStep.value = 1
|
||||
installShowDialog.value = true
|
||||
installAfterTips.value = []
|
||||
installCheckResult.value = res.data
|
||||
userStore.clearRouters()
|
||||
}).catch(() => { })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -582,7 +628,7 @@ const installAddonFn = (key: string) => {
|
||||
*/
|
||||
let notificationEl = null
|
||||
const getInstallTask = (first: boolean = true) => {
|
||||
getAddonInstalltask().then(res => {
|
||||
getAddonInstalltask().then((res) => {
|
||||
if (res.data) {
|
||||
if (first) {
|
||||
installLog = []
|
||||
@ -591,10 +637,10 @@ const getInstallTask = (first: boolean = true) => {
|
||||
notificationEl = ElNotification.success({
|
||||
title: t('warning'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: h('div', {}, [
|
||||
t('installingTips'),
|
||||
h('span', { class: 'text-primary cursor-pointer', onClick: checkInstallTask }, [t('installPercent')])
|
||||
]),
|
||||
message: h('div', {}, [t('installingTips'), h('span', {
|
||||
class: 'text-primary cursor-pointer',
|
||||
onClick: checkInstallTask
|
||||
}, [t('installPercent')])]),
|
||||
duration: 0,
|
||||
showClose: false
|
||||
})
|
||||
@ -638,7 +684,7 @@ const handleInstall = () => {
|
||||
if (!installCheckResult.value.is_pass || localInstalling.value) return
|
||||
localInstalling.value = true
|
||||
|
||||
installAddon({ addon: currAddon.value }).then(res => {
|
||||
installAddon({ addon: currAddon.value }).then((res) => {
|
||||
installStep.value = 3
|
||||
localListFn()
|
||||
localInstalling.value = false
|
||||
@ -662,27 +708,25 @@ const handleCloudInstall = () => {
|
||||
if (!installCheckResult.value.is_pass || cloudInstalling.value) return
|
||||
cloudInstalling.value = true
|
||||
|
||||
cloudInstallAddon({ addon: currAddon.value }).then(res => {
|
||||
cloudInstallAddon({ addon: currAddon.value })
|
||||
.then((res) => {
|
||||
installStep.value = 2
|
||||
terminalRef.value.execute('clear')
|
||||
terminalRef.value.execute('开始安装插件')
|
||||
getInstallTask()
|
||||
cloudInstalling.value = false
|
||||
}).catch((res) => {
|
||||
})
|
||||
.catch((res) => {
|
||||
cloudInstalling.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const authElMessageBox = () => {
|
||||
ElMessageBox.confirm(
|
||||
t('authTips'),
|
||||
t('warning'),
|
||||
{
|
||||
ElMessageBox.confirm(t('authTips'), t('warning'), {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: t('toBind'),
|
||||
cancelButtonText: t('toNiucloud')
|
||||
}
|
||||
).then(() => {
|
||||
}).then(() => {
|
||||
authCodeApproveFn()
|
||||
}).catch((action: string) => {
|
||||
if (action === 'cancel') {
|
||||
@ -693,13 +737,12 @@ const authElMessageBox = () => {
|
||||
|
||||
let installLog: string[] = []
|
||||
const getCloudInstallLog = () => {
|
||||
getAddonCloudInstallLog(currAddon.value)
|
||||
.then(res => {
|
||||
getAddonCloudInstallLog(currAddon.value).then((res) => {
|
||||
const data = res.data.data ?? []
|
||||
if (data[0] && data[0].length && installShowDialog.value == true) {
|
||||
data[0].forEach(item => {
|
||||
data[0].forEach((item) => {
|
||||
if (!installLog.includes(item.action)) {
|
||||
terminalRef.value.pushMessage({ content: `正在执行:${item.action}` })
|
||||
terminalRef.value.pushMessage({ content: `${ item.action }` })
|
||||
installLog.push(item.action)
|
||||
|
||||
if (item.code == 0) {
|
||||
@ -708,8 +751,7 @@ const getCloudInstallLog = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
}).catch(() => {
|
||||
notificationEl?.close()
|
||||
})
|
||||
}
|
||||
@ -729,17 +771,13 @@ const uninstallCheckResult = ref({})
|
||||
* @param key
|
||||
*/
|
||||
const uninstallAddonFn = (key: string) => {
|
||||
ElMessageBox.confirm(
|
||||
t('uninstallTips'),
|
||||
t('warning'),
|
||||
{
|
||||
ElMessageBox.confirm(t('uninstallTips'), t('warning'), {
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
}).then(() => {
|
||||
handleUninstallAddon(key)
|
||||
}).catch(() => { })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -762,13 +800,11 @@ const handleCloudBuild = () => {
|
||||
cloudBuildRef.value?.open()
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'),
|
||||
{
|
||||
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'), {
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
}).then(() => {
|
||||
cloudBuildRef.value?.open()
|
||||
})
|
||||
}
|
||||
@ -776,7 +812,7 @@ const handleCloudBuild = () => {
|
||||
const handleUninstallAddon = (key: string) => {
|
||||
preUninstallCheck(key).then(({ data }) => {
|
||||
if (data.is_pass) {
|
||||
uninstallAddon({ addon: key }).then(res => {
|
||||
uninstallAddon({ addon: key }).then((res) => {
|
||||
localListFn()
|
||||
userStore.clearRouters()
|
||||
loading.value = false
|
||||
@ -791,7 +827,7 @@ const handleUninstallAddon = (key: string) => {
|
||||
}
|
||||
|
||||
const market = () => {
|
||||
window.open(installPhpConfig.value.website_url)
|
||||
window.open('https://www.niucloud.com/app')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -800,34 +836,40 @@ const market = () => {
|
||||
*/
|
||||
const installShowDialogClose = (done: () => {}) => {
|
||||
if (installStep.value == 2) {
|
||||
ElMessageBox.confirm(
|
||||
t('installShowDialogCloseTips'),
|
||||
t('warning'),
|
||||
{
|
||||
ElMessageBox.confirm(t('installShowDialogCloseTips'), t('warning'), {
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
}).then(() => {
|
||||
cancelInstall(currAddon.value)
|
||||
done()
|
||||
}).catch(() => { })
|
||||
})
|
||||
} else if (installStep.value == 3) {
|
||||
activeNameTabFn('installed')
|
||||
location.reload()
|
||||
} else done()
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
|
||||
flashInterval && clearInterval(flashInterval)
|
||||
}
|
||||
|
||||
// 插件详情
|
||||
const appStoreShowDialog = ref(false)
|
||||
const appStoreInfo = ref<AnyObject>({})
|
||||
const getAddonDetialFn = (data: AnyObject) => {
|
||||
const appStoreInfo = ref({})
|
||||
const getAddonDetailFn = (data: any) => {
|
||||
appStoreShowDialog.value = true
|
||||
appStoreInfo.value = data
|
||||
}
|
||||
// 更新信息
|
||||
|
||||
const upgradeKey = ref<string>('')
|
||||
const updateInformationFn = (data: any) => {
|
||||
// updateInformationDialog.value = true
|
||||
|
||||
upgradeKey.value = data.key
|
||||
upgradeLogRef.value?.open()
|
||||
}
|
||||
// 授权
|
||||
const authCodeApproveDialog = ref(false)
|
||||
const authinfo = ref('')
|
||||
@ -836,18 +878,17 @@ const saveLoading = ref(false)
|
||||
const authLoading = ref(true)
|
||||
const checkAppMange = () => {
|
||||
authLoading.value = true
|
||||
getAuthInfo()
|
||||
.then((res) => {
|
||||
getAuthInfo().then((res) => {
|
||||
authLoading.value = false
|
||||
if (res.data.data && res.data.data.length != 0) {
|
||||
authinfo.value = res.data.data
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
}).catch(() => {
|
||||
authLoading.value = false
|
||||
authCodeApproveDialog.value = false
|
||||
})
|
||||
}
|
||||
|
||||
checkAppMange()
|
||||
const authCodeApproveFn = () => {
|
||||
authCodeApproveDialog.value = true
|
||||
@ -861,29 +902,23 @@ 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) => {
|
||||
if (saveLoading.value || !formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
await formEl.validate(async(valid) => {
|
||||
if (valid) {
|
||||
saveLoading.value = true
|
||||
|
||||
setAuthInfo(formData)
|
||||
.then(() => {
|
||||
setAuthInfo(formData).then(() => {
|
||||
saveLoading.value = false
|
||||
setTimeout(() => {
|
||||
location.reload()
|
||||
}, 1000)
|
||||
})
|
||||
.catch(() => {
|
||||
}).catch(() => {
|
||||
saveLoading.value = false
|
||||
})
|
||||
}
|
||||
@ -895,24 +930,40 @@ const goRouter = () => {
|
||||
}
|
||||
|
||||
const deleteAddonFn = (key: string) => {
|
||||
ElMessageBox.confirm(
|
||||
t('deleteAddonTips'),
|
||||
t('warning'),
|
||||
{
|
||||
ElMessageBox.confirm(t('deleteAddonTips'), t('warning'), {
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
}).then(() => {
|
||||
deleteAddonDevelop(key).then(() => {
|
||||
localListFn()
|
||||
})
|
||||
}).catch(() => { })
|
||||
})
|
||||
}
|
||||
|
||||
const versionJudge = (row: any) => {
|
||||
if (!row.support_version) return true
|
||||
const supportVersionApp = row.support_version.split('.')
|
||||
const frameworkVersionArr = frameworkVersion.value.split('.')
|
||||
if (parseFloat(`${ supportVersionApp[0] }.${ supportVersionApp[1] }`) < parseFloat(`${ frameworkVersionArr[0] }.${ frameworkVersionArr[1] }`)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
let batchUpgradeApp = []
|
||||
const handleSelectionChange = (e: any) => {
|
||||
batchUpgradeApp = e.map(item => item.key)
|
||||
}
|
||||
|
||||
const batchUpgrade = () => {
|
||||
if (!batchUpgradeApp.length) {
|
||||
ElMessage({ message: '请先勾选要升级的插件', type: 'error', duration: 5000 })
|
||||
return
|
||||
}
|
||||
upgradeAddonFn(batchUpgradeApp.toString())
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 多行超出隐藏 */
|
||||
.multi-hidden {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
@ -922,50 +973,6 @@ const deleteAddonFn = (key: string) => {
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.demo-tabs>.el-tabs__content {
|
||||
padding: 32px;
|
||||
color: #6b778c;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.plug-item {
|
||||
.plug-item-operate {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
font-size: var(--el-font-size-extra-small);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.t-container) {
|
||||
box-shadow: none !important;
|
||||
|
||||
.t-window {
|
||||
padding: 10px 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-btn.active {
|
||||
border-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.plug-large {
|
||||
.plug-item-operate {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
font-size: var(--el-font-size-extra-small);
|
||||
}
|
||||
}
|
||||
|
||||
.app-text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
// 插件安装-弹窗-表格样式
|
||||
.table-head-bg {
|
||||
background: #f5f7f9;
|
||||
@ -980,16 +987,13 @@ html.dark .table-head-bg {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.app-store {
|
||||
height: calc(100vh - 120px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:deep(.terminal .t-log-box span) {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
:deep(.data-loading) {
|
||||
.el-table__body-wrapper {
|
||||
display: none!important;
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -150,7 +150,6 @@ const getVerifyDetailFn = async () => {
|
||||
}
|
||||
|
||||
const setFormData = async (row: any = null) => {
|
||||
console.log("setFormData",row);
|
||||
code = row.code;
|
||||
getVerifyDetailFn();
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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>
|
||||
@ -49,7 +48,7 @@
|
||||
<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" />
|
||||
@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>
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
// 获取会员信息
|
||||
|
||||
@ -11,8 +11,12 @@
|
||||
<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>
|
||||
@ -27,7 +31,7 @@ import usePosterStore from '@/stores/modules/poster'
|
||||
const posterStore = usePosterStore()
|
||||
|
||||
// 图片形状改变
|
||||
const imgShapeChangeFn = (data)=>{
|
||||
const imgShapeChangeFn = (data) => {
|
||||
posterStore.editComponent.shape = data;
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<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"/>
|
||||
<upload-image v-model="posterStore.editComponent.value" :limit="1" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@ -15,7 +15,7 @@
|
||||
</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
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<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-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">
|
||||
@ -14,14 +14,14 @@
|
||||
</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"/>
|
||||
<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"/>
|
||||
<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>
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
<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'
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
</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;
|
||||
})
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
</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;
|
||||
})
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
</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;
|
||||
})
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
</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;
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
</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;
|
||||
})
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
</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;
|
||||
})
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
</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;
|
||||
})
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
</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;
|
||||
})
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
<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">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
|
||||
<h3 class="panel-title !text-sm">{{ t('printerSet') }}</h3>
|
||||
@ -83,8 +84,10 @@
|
||||
</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
|
||||
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>
|
||||
@ -113,24 +116,31 @@
|
||||
</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 = {
|
||||
const initialFormData: any = {
|
||||
printer_id: route.query.printer_id || 0,
|
||||
brand: '',
|
||||
printer_name: '',
|
||||
@ -143,14 +153,14 @@
|
||||
value: {},
|
||||
print_width: '58mm',
|
||||
status: 1,
|
||||
}
|
||||
}
|
||||
|
||||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
printer_name: [
|
||||
{ required: true, message: t('printerNamePlaceholder'), trigger: 'blur' },
|
||||
@ -171,11 +181,11 @@
|
||||
{ required: true, message: t('apikeyPlaceholder'), trigger: 'blur' },
|
||||
]
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const printerType = ref([])
|
||||
const printerType = ref([])
|
||||
|
||||
const init = async ()=> {
|
||||
const init = async() => {
|
||||
await getPrinterType({}).then((res: any) => {
|
||||
if (res.data) {
|
||||
printerType.value = res.data;
|
||||
@ -219,8 +229,8 @@
|
||||
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])
|
||||
for (let ck in formData[key]) {
|
||||
Object.assign(formData[key][ck], data[key][ck])
|
||||
}
|
||||
} else {
|
||||
formData[key] = data[key]
|
||||
@ -230,17 +240,17 @@
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init()
|
||||
init()
|
||||
|
||||
const brandList = ref([])
|
||||
getPrinterBrand({}).then((res: any) => {
|
||||
const brandList = ref([])
|
||||
getPrinterBrand({}).then((res: any) => {
|
||||
brandList.value = res.data;
|
||||
})
|
||||
})
|
||||
|
||||
const templateList:any = ref({}) // 小票打印模板列表
|
||||
getPrinterTemplateList({}).then((res:any)=> {
|
||||
const templateList: any = ref({}) // 小票打印模板列表
|
||||
getPrinterTemplateList({}).then((res: any) => {
|
||||
if (res.data) {
|
||||
let data = res.data;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
@ -254,13 +264,13 @@
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
/**
|
||||
* 确认
|
||||
* @param formEl
|
||||
*/
|
||||
const confirm = async(formEl: FormInstance | undefined) => {
|
||||
const confirm = async(formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
|
||||
if (printerType.value.length == 0) {
|
||||
@ -353,11 +363,11 @@
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const back = () => {
|
||||
const back = () => {
|
||||
router.push('/printer/list')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@ -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">
|
||||
@ -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: ''
|
||||
}
|
||||
})
|
||||
|
||||
@ -111,12 +110,13 @@ const loadPrinterList = (page: number = 1) => {
|
||||
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)
|
||||
|
||||
|
||||
@ -33,10 +33,15 @@
|
||||
<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" />
|
||||
<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" />
|
||||
<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'">
|
||||
@ -59,7 +64,7 @@
|
||||
<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="small" />-->
|
||||
<el-option label="大" value="big" />
|
||||
</el-select>
|
||||
</div>
|
||||
@ -93,7 +98,7 @@
|
||||
<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>
|
||||
|
||||
@ -111,34 +116,34 @@
|
||||
</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 = {
|
||||
const initialFormData: any = {
|
||||
template_id: route.query.template_id || 0,
|
||||
template_type: '',
|
||||
template_name: '',
|
||||
value: {},
|
||||
}
|
||||
}
|
||||
|
||||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
// 表单验证规则
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
template_name: [
|
||||
{ required: true, message: t('templateNamePlaceholder'), trigger: 'blur' },
|
||||
@ -147,25 +152,25 @@
|
||||
{ required: true, message: t('templateTypePlaceholder'), trigger: 'blur' },
|
||||
]
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 动态加载组件
|
||||
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 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 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 previewPath = ref('')
|
||||
const printerType: any = ref([])
|
||||
const template: any = ref([])
|
||||
|
||||
const init = async ()=> {
|
||||
const init = async() => {
|
||||
await getPrinterType({}).then((res: any) => {
|
||||
if (res.data && res.data.length) {
|
||||
printerType.value = res.data;
|
||||
@ -176,39 +181,39 @@
|
||||
}
|
||||
})
|
||||
|
||||
if(formData.template_id) {
|
||||
getPrinterTemplateInfo(formData.template_id).then((res:any)=>{
|
||||
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])
|
||||
for (let ck in formData[key]) {
|
||||
Object.assign(formData[key][ck], data[key][ck])
|
||||
}
|
||||
} else {
|
||||
formData[key] = data[key]
|
||||
}
|
||||
})
|
||||
loading.value = false
|
||||
}else {
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
duration: 1500,
|
||||
message: t('printTemplateEmpty')
|
||||
})
|
||||
setTimeout(()=>{
|
||||
setTimeout(() => {
|
||||
back();
|
||||
loading.value = false
|
||||
}, 2000);
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
init();
|
||||
init();
|
||||
|
||||
// 切换模板类型
|
||||
const handlePrintType = (value: any, load: boolean = false) => {
|
||||
// 切换模板类型
|
||||
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;
|
||||
@ -224,10 +229,10 @@
|
||||
}
|
||||
|
||||
refreshTemplateData();
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新模板内容数据
|
||||
const refreshTemplateData = ()=> {
|
||||
// 刷新模板内容数据
|
||||
const refreshTemplateData = () => {
|
||||
for (let i = 0; i < template.value.length; i++) {
|
||||
let item: any = template.value[i];
|
||||
formData.value[item.key] = {};
|
||||
@ -242,13 +247,13 @@
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 确认
|
||||
* @param formEl
|
||||
*/
|
||||
const confirm = async(formEl: FormInstance | undefined) => {
|
||||
const confirm = async(formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
|
||||
if (printerType.value.length == 0) {
|
||||
@ -339,11 +344,11 @@
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const back = () => {
|
||||
const back = () => {
|
||||
router.push('/printer/template/list')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -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)
|
||||
|
||||
/**
|
||||
* 添加小票打印模板
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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>({})
|
||||
|
||||
/**
|
||||
* 保存
|
||||
|
||||
@ -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(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -65,12 +65,8 @@ import { reactive, ref, computed } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getLoginConfig, setLoginConfig } from '@/app/api/member'
|
||||
import { FormInstance } from 'element-plus'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
|
||||
const loading = ref(true)
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
const formData:any = reactive({
|
||||
|
||||
@ -85,10 +85,6 @@ import { getNoticeList } from '@/app/api/notice'
|
||||
import Sms from '@/app/views/setting/components/notice-sms.vue'
|
||||
import Wechat from '@/app/views/setting/components/notice-wechat.vue'
|
||||
import Weapp from '@/app/views/setting/components/notice-weapp.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
|
||||
const smsDialog : Record<string, any> | null = ref(null)
|
||||
const wechatDialog : Record<string, any> | null = ref(null)
|
||||
@ -153,9 +149,17 @@ loadNoticeList()
|
||||
|
||||
const setNotice = (data : any, type : string) => {
|
||||
data.type = type
|
||||
eval('data.status=data.is_' + type)
|
||||
eval(type + 'Dialog.value.setFormData(data)')
|
||||
eval(type + 'Dialog.value.showDialog = true;')
|
||||
data.status = data['is_' + type]
|
||||
if (type === 'sms') {
|
||||
smsDialog.value.setFormData(data)
|
||||
smsDialog.value.showDialog = true
|
||||
} else if (type === 'wechat') {
|
||||
wechatDialog.value.setFormData(data)
|
||||
wechatDialog.value.showDialog = true
|
||||
} else if (type === 'weapp') {
|
||||
weappDialog.value.setFormData(data)
|
||||
weappDialog.value.showDialog = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -134,7 +134,6 @@ const setConfigInfo = (data:any) => {
|
||||
element.config = data.config
|
||||
}
|
||||
})
|
||||
console.log(payConfigData.value)
|
||||
}
|
||||
|
||||
// 初始化配置信息
|
||||
@ -157,10 +156,6 @@ const enablePaymentMode = async (data: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
interface SortableEvt extends SortableEvent {
|
||||
originalEvent?: DragEvent
|
||||
}
|
||||
|
||||
// 拖动
|
||||
const fieldBoxRefs = ref<any>([])
|
||||
watch(isEdit, (newValue, oldValue) => {
|
||||
|
||||
@ -142,7 +142,6 @@ const save = async (formEl: FormInstance | undefined) => {
|
||||
|
||||
setWebsite(formData).then(() => {
|
||||
loading.value = false
|
||||
appType.value == 'admin' ? useSystemStore().getWebsiteInfo() : useUserStore().getSiteInfo()
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
@ -40,6 +40,17 @@
|
||||
</div>
|
||||
<div class="form-tip">{{ t('mchPublicCertPathTips') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('wechatpayPublicCert')" prop="wechatpay_config.wechat_public_cert_path">
|
||||
<div class="input-width">
|
||||
<upload-file v-model="formData.wechatpay_config.wechat_public_cert_path" api="sys/document/wechat" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('wechatpayPublicCertId')" prop="wechatpay_config.wechat_public_cert_id">
|
||||
<div class="input-width">
|
||||
<el-input v-model.trim="formData.wechatpay_config.wechat_public_cert_id" placeholder="" class="input-width" show-word-limit clearable />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
|
||||
<!-- <el-card class="box-card mt-[15px] !border-none" shadow="never">
|
||||
@ -98,7 +109,9 @@ const initialFormData = {
|
||||
mch_id: '',
|
||||
mch_secret_key: '',
|
||||
mch_secret_cert: '',
|
||||
mch_public_cert_path: ''
|
||||
mch_public_cert_path: '',
|
||||
wechat_public_cert_path: '',
|
||||
wechat_public_cert_id: ''
|
||||
},
|
||||
alipay_config: {
|
||||
app_secret_cert: '',
|
||||
|
||||
@ -205,7 +205,6 @@ const getAddonDevelopCheckFn = (key: any) => {
|
||||
// autofocus: false,
|
||||
confirmButtonText: t('confirm'),
|
||||
callback: (action: any) => {
|
||||
console.log(action)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -29,7 +29,7 @@ const type = ref(attachmentType[0])
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.attachment-container {
|
||||
.attachment-container {
|
||||
overflow: hidden;
|
||||
min-height: calc(100vh - 94px);
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
@ -71,5 +71,5 @@ const type = ref(attachmentType[0])
|
||||
padding: 0 0 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
796
admin/src/app/views/tools/backup_records.vue
Normal file
796
admin/src/app/views/tools/backup_records.vue
Normal file
@ -0,0 +1,796 @@
|
||||
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
<el-button type="primary" @click="manualBackupEvent">
|
||||
{{ t('manualBackup') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('content')" prop="content">
|
||||
<el-input v-model.trim="tableData.searchParam.content" :placeholder="t('contentPlaceholder')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div class="mb-[10px] flex items-center">
|
||||
<el-button @click="batchDelete" size="small">{{ t('batchDelete') }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="tableData.data" size="large" v-loading="tableData.loading" ref="tableRef" @selection-change="handleSelectionChange">
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="id" :label="t('id')" width="120" />
|
||||
<el-table-column prop="content" :label="t('content')" width="120" />
|
||||
<el-table-column prop="version" :label="t('currentVersion')" width="120" />
|
||||
<el-table-column prop="backup_dir" :label="t('backupDir')" width="220" />
|
||||
<el-table-column prop="complete_time" :label="t('completeTime')" width="220" />
|
||||
<el-table-column prop="remark" :label="t('remark')">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.remark" class="multi-hidden">{{ row.remark }}</span>
|
||||
<span v-else>{{ t('remarkEmpty') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="200">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="remarkEvent(row)">{{ t('remark') }}</el-button>
|
||||
<el-button type="primary" link @click="restoreEvent(row)">{{ t('restore') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="tableData.page"
|
||||
v-model:page-size="tableData.limit" layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total" @size-change="loadList()"
|
||||
@current-change="loadList" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="showDialog" :title="iSBackupRecovery == 1 ? t('manualBackupTitle') : t('restoreTitle')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="true" :before-close="dialogClose">
|
||||
|
||||
<el-steps :active="numberOfSteps" align-center class="number-of-steps" finish-status="success" process-status="process">
|
||||
<template v-if="iSBackupRecovery == 1">
|
||||
<!-- 手动备份 -->
|
||||
<el-step :title="t('testDirectoryPermissions')" />
|
||||
<el-step :title="t('startBackUp')" />
|
||||
<el-step :title="t('backUpEnd')" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- 恢复 -->
|
||||
<el-step :title="t('testDirectoryPermissions')" />
|
||||
<el-step :title="t('startUpgrade')" />
|
||||
<el-step :title="t('upgradeEnd')" />
|
||||
</template>
|
||||
</el-steps>
|
||||
|
||||
<div class="h-[400px]" style="overflow: auto">
|
||||
|
||||
<!-- 检测目录权限 -->
|
||||
<div class="flex flex-col" v-show="active == 'check'">
|
||||
<el-scrollbar>
|
||||
<div class="bg-[#fff] my-3">
|
||||
<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="18">
|
||||
<span>{{ t("upgrade.path") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span>{{ t("upgrade.demand") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span>{{ t("status") }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="height: calc(300px); overflow: auto" v-if="upgradeCheck && upgradeCheck.dir">
|
||||
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_readable">
|
||||
<el-col :span="18">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span :class="{ 'mx-[10px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">{{ t("upgrade.readable") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span v-if="item.status" :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||
<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="18">
|
||||
<span>{{ item.dir }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span :class="{ 'mx-[10px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">{{ t("upgrade.write") }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span v-if="item.status" :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||
<el-icon color="green">
|
||||
<Select />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else :class="{ 'mx-[20px]' : (upgradeCheck.dir.is_readable.length + upgradeCheck.dir.is_write.length) > 9 }">
|
||||
<el-icon color="red">
|
||||
<CloseBold />
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-loading="true" style="height: calc(300px); overflow: auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- 执行任务 -->
|
||||
<div class="h-[370px] mt-[30px]" v-if="active == 'execute'">
|
||||
<terminal ref="terminalRef" context="" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd"/>
|
||||
</div>
|
||||
|
||||
<!-- 完成 -->
|
||||
<div class="mt-[50px]" v-if="active == 'complete'">
|
||||
<el-result icon="success" :title="iSBackupRecovery == 1 ? t('backupCompleteTips') : t('restoreCompleteTips')"></el-result>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" v-if="active == 'check'">
|
||||
<!-- 手动备份 -->
|
||||
<el-button v-if="iSBackupRecovery == 1" type="primary" :loading="uploading" :disabled="isPass" @click="manualBackupFn()">{{ t("nextStep") }}</el-button>
|
||||
|
||||
<!-- 恢复 -->
|
||||
<el-button v-else type="primary" :loading="uploading" :disabled="isPass" @click="restoreUpgradeBackupFn(currentId)">{{ t("nextStep") }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="showRemarkDialog" :title="t('remark')" width="460px" :destroy-on-close="true">
|
||||
<el-form :model="formData" ref="formRef" class="page-form" v-loading="remarkLoading">
|
||||
<el-form-item class="mb-0">
|
||||
<el-input v-model.trim="formData.remark" :rows="5" type="textarea" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showRemarkDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="remarkLoading" @click="modifyRemarkFn()">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, nextTick, watch, h } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { checkDirExist, checkPermission, getBackupRecords, restoreUpgradeBackup, deleteRecords, modifyBackupRemark, manualBackup, performBackupTasks, performRecoveryTasks } from '@/app/api/upgrade'
|
||||
import { Terminal, TerminalFlash } from 'vue-web-terminal'
|
||||
import 'vue-web-terminal/lib/theme/dark.css'
|
||||
import { AnyObject } from '@/types/global'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
const multipleSelection: any = ref([]) // 选中数据
|
||||
const tableRef = ref()
|
||||
const repeat = ref(false)
|
||||
const cloudBuildTask = ref<null | AnyObject>(null)
|
||||
const tableData: any = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
content: ''
|
||||
}
|
||||
})
|
||||
|
||||
const showDialog: any = ref<boolean>(false)
|
||||
const active = ref('check')
|
||||
const interrupt: any = ref(false) // 是否中断
|
||||
const upgradeCheck = ref<null | AnyObject>(null)
|
||||
const terminalRef: any = ref(null)
|
||||
const cloudBuildLog: any = []
|
||||
let notificationEl: any = null
|
||||
const isPass: any = ref(false)
|
||||
const uploading: any = ref(false)
|
||||
const numberOfSteps = ref(0)
|
||||
const currentId: any = ref(0)
|
||||
let backupContents = []
|
||||
let restoreContents = []
|
||||
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
|
||||
formEl.resetFields()
|
||||
loadList()
|
||||
}
|
||||
|
||||
// 监听表格单行选中
|
||||
const handleSelectionChange = (val: []) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*/
|
||||
const loadList = (page: number = 1) => {
|
||||
tableData.loading = true
|
||||
tableData.page = page
|
||||
getBackupRecords({
|
||||
page: tableData.page,
|
||||
limit: tableData.limit,
|
||||
...tableData.searchParam
|
||||
}).then(res => {
|
||||
tableData.loading = false
|
||||
tableData.data = res.data.data
|
||||
tableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
tableData.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
loadList()
|
||||
const iSBackupRecovery = ref(0) // 1:手动备份 2:恢复
|
||||
|
||||
// 手动备份
|
||||
const manualBackupEvent = () => {
|
||||
ElMessageBox.confirm(t('manualBackupTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
// if (repeat.value) return
|
||||
// repeat.value = true
|
||||
backupContents = []
|
||||
iSBackupRecovery.value = 1
|
||||
showDialog.value = true
|
||||
uploading.value = true
|
||||
active.value = 'check'
|
||||
interrupt.value = false
|
||||
checkPermissionFn()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 进入执行备份
|
||||
const manualBackupFn = (task: any = '') => {
|
||||
// 执行一半,中途取消,拦截执行
|
||||
if (interrupt.value) return
|
||||
|
||||
if (task == '') {
|
||||
numberOfSteps.value = 1
|
||||
active.value = 'execute'
|
||||
}
|
||||
manualBackup({ task }).then((res: any) => {
|
||||
const data = res.data
|
||||
if (task == '') {
|
||||
terminalRef.value.execute('clear')
|
||||
terminalRef.value.execute('开始执行')
|
||||
}
|
||||
if (data.content && !backupContents.includes(data.content)) {
|
||||
backupContents.push(data.content)
|
||||
terminalRef.value.pushMessage({ content: `${ data.content }` })
|
||||
}
|
||||
if (data.task == 'end') {
|
||||
numberOfSteps.value = 2
|
||||
setTimeout(() => {
|
||||
numberOfSteps.value = 3
|
||||
active.value = 'complete'
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 1500)
|
||||
} else if (data.task == 'fail') {
|
||||
// 恢复失败
|
||||
setTimeout(() => {
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 2000)
|
||||
} else {
|
||||
// 延迟2秒请求,等待恢复数据加载完成
|
||||
setTimeout(() => {
|
||||
manualBackupFn(data.task)
|
||||
}, 2000)
|
||||
}
|
||||
}).catch(() => {
|
||||
repeat.value = false
|
||||
tableData.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询备份任务
|
||||
*/
|
||||
const getCloudBuildTaskFn = () => {
|
||||
performBackupTasks({}).then(({ data }) => {
|
||||
if (!data) return
|
||||
|
||||
cloudBuildTask.value = data
|
||||
|
||||
if (!showDialog.value && data.data && data.data.length > 0) {
|
||||
showElNotification()
|
||||
}
|
||||
}).catch()
|
||||
}
|
||||
|
||||
// 注释获取任务 后续改
|
||||
// getCloudBuildTaskFn()
|
||||
/**
|
||||
* 备份中任务提示
|
||||
*/
|
||||
const showElNotification = () => {
|
||||
notificationEl = ElNotification.success({
|
||||
title: t('warning'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: h('div', {}, [
|
||||
t('cloudbuild.executingTips'),
|
||||
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('cloudbuild.clickView')])
|
||||
]),
|
||||
duration: 0,
|
||||
showClose: false
|
||||
})
|
||||
}
|
||||
|
||||
// 点击进入备份窗口
|
||||
const elNotificationClick = () => {
|
||||
iSBackupRecovery.value = 1
|
||||
showDialog.value = true
|
||||
nextTick(() => {
|
||||
notificationEl && notificationEl.close()
|
||||
terminalRef.value.execute('clear')
|
||||
terminalRef.value.execute('开始执行')
|
||||
getCloudBuildLogFn()
|
||||
})
|
||||
}
|
||||
|
||||
// 窗口list展示
|
||||
const getCloudBuildLogFn = () => {
|
||||
performBackupTasks({}).then(({ data }) => {
|
||||
if (!data) return
|
||||
cloudBuildTask.value = data
|
||||
cloudBuildTask.value.data.forEach(item => {
|
||||
if (!cloudBuildLog.includes(item.content)) {
|
||||
terminalRef.value.pushMessage({ content: `${item.content}` })
|
||||
cloudBuildLog.push(item.content)
|
||||
}
|
||||
})
|
||||
|
||||
const lastTask = data.data[data.data.length - 1].task
|
||||
if (lastTask === 'end' || data.data.length == 0) {
|
||||
setTimeout(() => {
|
||||
active.value = 'complete'
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 1500)
|
||||
} else if (lastTask === 'fail') {
|
||||
// 恢复失败
|
||||
setTimeout(() => {
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 2000)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
getCloudBuildLogFn()
|
||||
}, 2000)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 恢复备份
|
||||
const restoreEvent = (data: any) => {
|
||||
ElMessageBox.confirm(t('restoreTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
// if (repeat.value) return
|
||||
// repeat.value = true
|
||||
restoreContents = []
|
||||
iSBackupRecovery.value = 2
|
||||
currentId.value = data.id
|
||||
active.value = 'check'
|
||||
interrupt.value = false
|
||||
checkDirExistFn(data.id)
|
||||
})
|
||||
}
|
||||
|
||||
// 检测目录是否存在
|
||||
const checkDirExistFn = (id: any) => {
|
||||
checkDirExist({ id }).then(({ data }) => {
|
||||
if (data) {
|
||||
showDialog.value = true
|
||||
uploading.value = true
|
||||
checkPermissionFn()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 检测目录权限
|
||||
const checkPermissionFn = () => {
|
||||
checkPermission({}).then(({ data }) => {
|
||||
upgradeCheck.value = data
|
||||
isPass.value = !data.is_pass
|
||||
uploading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 执行恢复备份
|
||||
const restoreUpgradeBackupFn = (id: any, task: any = '') => {
|
||||
// 执行一半,中途取消,拦截执行
|
||||
if (interrupt.value) return
|
||||
if (task == '') {
|
||||
numberOfSteps.value = 1
|
||||
active.value = 'execute'
|
||||
}
|
||||
|
||||
restoreUpgradeBackup({
|
||||
id,
|
||||
task
|
||||
}).then((res: any) => {
|
||||
const data = res.data
|
||||
if (task == '') {
|
||||
uploading.value = false
|
||||
terminalRef.value.execute('clear')
|
||||
terminalRef.value.execute('开始执行')
|
||||
}
|
||||
if (data.content && !restoreContents.includes(data.content)) {
|
||||
restoreContents.push(data.content)
|
||||
terminalRef.value.pushMessage({ content: `${ data.content }` })
|
||||
}
|
||||
if (data.task == 'end') {
|
||||
numberOfSteps.value = 2
|
||||
setTimeout(() => {
|
||||
numberOfSteps.value = 3
|
||||
active.value = 'complete'
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 1500)
|
||||
} else if (data.task == 'fail') {
|
||||
// 恢复失败
|
||||
setTimeout(() => {
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 2000)
|
||||
} else {
|
||||
// 延迟2秒请求,等待恢复数据加载完成
|
||||
setTimeout(() => {
|
||||
restoreContents = []
|
||||
restoreUpgradeBackupFn(id, data.task)
|
||||
}, 2000)
|
||||
}
|
||||
}).catch(() => {
|
||||
repeat.value = false
|
||||
tableData.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询恢复任务
|
||||
*/
|
||||
const resumeUpgradeTasks = () => {
|
||||
performRecoveryTasks({}).then(({ data }) => {
|
||||
if (!data) return
|
||||
|
||||
cloudBuildTask.value = data
|
||||
|
||||
if (!showDialog.value && data.data && data.data.length > 0) {
|
||||
recoveryTaskPrompt()
|
||||
}
|
||||
}).catch()
|
||||
}
|
||||
// 注释获取任务 后续改
|
||||
// resumeUpgradeTasks()
|
||||
/**
|
||||
* 恢复中任务提示
|
||||
*/
|
||||
|
||||
const recoveryTaskPrompt = () => {
|
||||
notificationEl = ElNotification.success({
|
||||
title: t('warning'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: h('div', {}, [
|
||||
t('cloudbuild.executingTips'),
|
||||
h('span', { class: 'text-primary cursor-pointer', onClick: recoveryTaskPromptClick }, [t('cloudbuild.clickView')])
|
||||
]),
|
||||
duration: 0,
|
||||
showClose: false
|
||||
})
|
||||
}
|
||||
|
||||
// 点击任务提示进入窗口
|
||||
const recoveryTaskPromptClick = () => {
|
||||
iSBackupRecovery.value = 2
|
||||
showDialog.value = true
|
||||
nextTick(() => {
|
||||
notificationEl && notificationEl.close()
|
||||
terminalRef.value.execute('clear')
|
||||
terminalRef.value.execute('开始执行')
|
||||
restoreTaskList()
|
||||
})
|
||||
}
|
||||
|
||||
// 进入恢复窗口列表
|
||||
const restoreTaskList = () => {
|
||||
performRecoveryTasks({}).then(({ data }) => {
|
||||
if (!data) return
|
||||
cloudBuildTask.value = data
|
||||
cloudBuildTask.value.data.forEach(item => {
|
||||
if (!cloudBuildLog.includes(item.content)) {
|
||||
terminalRef.value.pushMessage({ content: `${item.content}` })
|
||||
cloudBuildLog.push(item.content)
|
||||
}
|
||||
})
|
||||
|
||||
const lastTask = data.data[data.data.length - 1].task
|
||||
if (lastTask === 'end' || data.data.length == 0) {
|
||||
setTimeout(() => {
|
||||
active.value = 'complete'
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 1500)
|
||||
} else if (lastTask === 'fail') {
|
||||
// 恢复失败
|
||||
setTimeout(() => {
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}, 2000)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
restoreTaskList()
|
||||
}, 2000)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const dialogClose = (done: () => {}) => {
|
||||
if (active.value == 'execute') {
|
||||
ElMessageBox.confirm(
|
||||
t('showDialogCloseTips'),
|
||||
t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
terminalRef.value.execute('clear')
|
||||
interrupt.value = true // 执行一半,中途取消,需要恢复初始状态
|
||||
done()
|
||||
}).catch(() => {
|
||||
})
|
||||
} else {
|
||||
if (active.value == 'complete') {
|
||||
// 恢复备份需要等待恢复数据加载完成,延迟刷新页面
|
||||
setTimeout(() => {
|
||||
location.reload()
|
||||
}, 500)
|
||||
}
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级进度动画
|
||||
*/
|
||||
let flashInterval: null | number = null
|
||||
const terminalFlash = new TerminalFlash()
|
||||
const onExecCmd = (key, command, success, failed, name) => {
|
||||
if (command == '开始执行') {
|
||||
success(terminalFlash)
|
||||
const frames = makeIterator(['/', '——', '\\', '|'])
|
||||
flashInterval = setInterval(() => {
|
||||
terminalFlash.flush('> ' + frames.next().value)
|
||||
}, 150)
|
||||
}
|
||||
}
|
||||
|
||||
const makeIterator = (array: string[]) => {
|
||||
let nextIndex = 0
|
||||
return {
|
||||
next () {
|
||||
if ((nextIndex + 1) == array.length) {
|
||||
nextIndex = 0
|
||||
}
|
||||
return { value: array[nextIndex++] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => showDialog.value, () => {
|
||||
if (!showDialog.value) {
|
||||
active.value = 'execute'
|
||||
flashInterval && clearInterval(flashInterval)
|
||||
}
|
||||
})
|
||||
|
||||
const showRemarkDialog: any = ref<boolean>(false)
|
||||
const remarkLoading = ref(false)
|
||||
const formData: any = reactive({
|
||||
id: 0,
|
||||
remark: ''
|
||||
})
|
||||
|
||||
// 修改备注
|
||||
const remarkEvent = (row: any) => {
|
||||
formData.id = row.id
|
||||
formData.remark = row.remark
|
||||
showRemarkDialog.value = true
|
||||
}
|
||||
|
||||
const modifyRemarkFn = () => {
|
||||
remarkLoading.value = true
|
||||
modifyBackupRemark({
|
||||
id: formData.id,
|
||||
remark: formData.remark
|
||||
}).then(() => {
|
||||
showRemarkDialog.value = false
|
||||
remarkLoading.value = false
|
||||
loadList()
|
||||
}).catch(() => {
|
||||
remarkLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 删除升级记录
|
||||
const deleteEvent = (id: number) => {
|
||||
ElMessageBox.confirm(t('deleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
if (repeat.value) return
|
||||
repeat.value = true
|
||||
tableData.loading = true
|
||||
deleteRecords({
|
||||
ids: id
|
||||
}).then(() => {
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}).catch(() => {
|
||||
repeat.value = false
|
||||
tableData.loading = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除升级记录
|
||||
const batchDelete = () => {
|
||||
if (multipleSelection.value.length == 0) {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: `${t('batchEmptySelectedTips')}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(t('deleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
tableData.loading = true
|
||||
if (repeat.value) return
|
||||
repeat.value = true
|
||||
|
||||
const ids: any = []
|
||||
multipleSelection.value.forEach((item: any) => {
|
||||
ids.push(item.id)
|
||||
})
|
||||
|
||||
deleteRecords({
|
||||
ids
|
||||
}).then(() => {
|
||||
loadList()
|
||||
repeat.value = false
|
||||
}).catch(() => {
|
||||
repeat.value = false
|
||||
tableData.loading = false
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.terminal .t-log-box span) {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.table-head-bg {
|
||||
background-color: var(--el-table-header-bg-color);
|
||||
}
|
||||
|
||||
::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;
|
||||
}
|
||||
}
|
||||
/* 多行超出隐藏 */
|
||||
.multi-hidden {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
218
admin/src/app/views/tools/cloud_compile.vue
Normal file
218
admin/src/app/views/tools/cloud_compile.vue
Normal file
@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div v-loading="loading" class="main-container w-full">
|
||||
<div class="p-5 bg-[#fff] overflow-hidden">
|
||||
|
||||
<div class="bg-[#fff] w-[100%] rounded-[8px] overflow-hidden">
|
||||
<div class=" relative pb-[13px]" style="border-bottom: 2px solid #f0f2f6">
|
||||
<div class="w-[66px] bg-primary h-[2px] absolute bottom-[0px]"></div>
|
||||
<span class=" text-primary text-[18px] ml-[4px]">云编译</span>
|
||||
</div>
|
||||
<div class="flex mt-[20px] ml-[20px]">
|
||||
<el-button class="w-[98px] !h-[36px]" type="primary" @click="handleCloudBuild" :loading="cloudBuildRef?.loading">云编译</el-button>
|
||||
<div class="btn-time w-[181px] h-[36px] rounded-[4px] text-[#606266] text-[14px] ml-[10px]">
|
||||
<span>云编译执行时间大约</span>
|
||||
<span class="text-[16px] text-[#D43030] mx-[3px]">3</span>
|
||||
<span>分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[21px] flex mb-[21px] items-center">
|
||||
<span class="flex ml-[20px] text-[16px] items-center">
|
||||
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||
温馨提示
|
||||
</span>
|
||||
<span class="text-[14px] text-[#606266] ml-[7px]"> 以下情况可以进行云编译</span>
|
||||
</div>
|
||||
|
||||
<div class="text-[14px] text-[#606266] ml-[13px] mb-[18px]">云编译不需要本地安装node环境即可进行,针对使用者方便快捷</div>
|
||||
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">1、系统或插件,每次安装或升级完成后,需要云编译</div>
|
||||
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">2、开发者编写完前端代码之后,可以使用云编译进行源码编译</div>
|
||||
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">3、由于云编译不是针对某个插件进行编译,而是系统整体编译,因此如果同时需要安装多个插件时,往往需要安装到最后一个插件才整体进行云编译</div>
|
||||
<div class="mt-[21px] flex mb-[21px] text-[16px] items-center">
|
||||
<span class="flex ml-[20px] items-center">
|
||||
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||
云编译流程
|
||||
</span>
|
||||
</div>
|
||||
<div class="ml-[40px]">
|
||||
<el-timeline>
|
||||
<el-timeline-item color="#4268EF">
|
||||
<template #dot>
|
||||
<div class="w-[15px] h-[15px] bg-primary rounded-[50%] text-[9px] text-[#fff] flex items-center justify-center">1</div>
|
||||
</template>
|
||||
<div class="text-[16px] text-[#303133]">编译admin代码</div>
|
||||
<div class="py-[12px] px-[10px] bg-[#F7F8FA] mt-[10px] text-[#606266] text-[14px] w-[1085px]">
|
||||
<span>云编译会将admin端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud 下的</span>
|
||||
<span class="text-[#FF9D31] mx-[3px]">public/admin</span>
|
||||
<span>目录中。后台的访问路径将变为</span>
|
||||
<span class="text-primary ml-[3px]">https://域名/admin</span>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item color="#4268EF">
|
||||
<template #dot>
|
||||
<div class="w-[15px] h-[15px] bg-primary rounded-[50%] text-[9px] text-[#fff] flex items-center justify-center">2</div>
|
||||
</template>
|
||||
<div class="text-[16px] text-[#303133]">编译uniapp代码</div>
|
||||
<div class="py-[12px] px-[10px] bg-[#F7F8FA] mt-[10px] text-[#606266] text-[14px] w-[1085px]">
|
||||
<span>云编泽会将uniapp端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
||||
<span class="text-[#FF9D31] mx-[3px]">public/wap</span>
|
||||
<span>目录中,这样手机端网页的访问路径将变为</span>
|
||||
<span class="text-primary ml-[3px]"> https://域名/wap</span>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item color="#4268EF">
|
||||
<template #dot>
|
||||
<div class="w-[15px] h-[15px] bg-primary rounded-[50%] text-[9px] text-[#fff] flex items-center justify-center">3</div>
|
||||
</template>
|
||||
<div class="text-[16px] text-[#303133]">编译web代码</div>
|
||||
<div class="py-[12px] px-[10px] bg-[#F7F8FA] mt-[10px] text-[#606266] text-[14px] w-[1085px]">
|
||||
<span>云编泽会将web端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
||||
<span class="text-[#FF9D31] mx-[3px]">public/web</span>
|
||||
<span>目录中,这样电脑端网页的访问路径将变为</span>
|
||||
<span class="text-primary ml-[3px]"> https://域名/web</span>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-5 bg-[#fff] mt-[26px]">
|
||||
<div class="bg-[#fff] w-[100%] rounded-[8px] overflow-hidden">
|
||||
<div class="relative pb-[13px]" style="border-bottom: 2px solid #f0f2f6">
|
||||
<div class="w-[85px] bg-primary h-[2px] absolute bottom-[0px]"></div>
|
||||
<span class="text-primary text-[18px] ml-[4px]">本地编译</span>
|
||||
</div>
|
||||
<div class="mt-[21px] flex mb-[21px] text-[16px] items-center">
|
||||
<span class="flex ml-[20px] items-center">
|
||||
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||
温馨提示
|
||||
</span>
|
||||
</div>
|
||||
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">
|
||||
<span>1、如果本地安装了Node环境,可以进行本地编译,要求</span>
|
||||
<span class="text-[#D43030] ml-[3px]">Node版本>18</span>
|
||||
</div>
|
||||
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">2、默认本地编译流程与云编译相同,执行本地编译命令后,会将编译后的代码移动到系统niucloud下的public下的对应端口目录下</div>
|
||||
<div class="ml-[40px] text-[14px] text-[#606266] mb-[18px]">3、由于云编译配置的访问路径时固定的,针对客户有独立部署admin,wap,web等个性化端口名称配置需求,需要进行本地编译</div>
|
||||
<div class="mt-[34px] flex mb-[21px] text-[16px] items-center">
|
||||
<span class="flex ml-[20px] items-center">
|
||||
<i class="w-[3px] h-[12px] bg-primary mr-[6px] block"></i>
|
||||
本地编译命令参考
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||
<span class=" text-[#303133]">安装依赖:</span>
|
||||
进入admin端与uniapp端以及web端目录都可执行
|
||||
</div>
|
||||
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm install</span>
|
||||
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm install')">复制命令</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[21px]">
|
||||
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||
<span class="text-[#303133]">后台admin端口打包:</span>
|
||||
<span>进入admin目录下执行,执行后编译代码默认移动到系统的niucloud下的</span>
|
||||
<span class="text-[#FF9D31] mx-[3px]">public/admin</span>
|
||||
<span>目录下</span>
|
||||
</div>
|
||||
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run build</span>
|
||||
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build')">复制命令</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[21px]">
|
||||
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||
<span class="text-[#303133]">使用uniapp打包H5:</span>
|
||||
<span>进入uniapp目录下执行,执行后编译代码默认移动到系统niucloud下的</span>
|
||||
<span class="text-[#FF9D31] mx-[3px]">public/wap</span>
|
||||
<span>目录下</span>
|
||||
</div>
|
||||
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run build:h5</span>
|
||||
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build:h5')">复制命令</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[21px]">
|
||||
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||
<span class=" text-[#303133]">使用uniapp打包微信小程序:</span>
|
||||
<span>进入uniapp目录下执行,执行后编译代码默认移动到系统niucloud下的</span>
|
||||
<span class="text-[#FF9D31] mx-[3px]">uni-app/dist/build/mp-weixin</span>
|
||||
<span>目录</span>
|
||||
</div>
|
||||
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run build:mp-weixin</span>
|
||||
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build:mp-weixin')">复制命令</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[21px]">
|
||||
<div class="ml-[40px] text-[14px] text-[#606266]">
|
||||
<span class="text-[#303133]">web端打包:</span>
|
||||
<span>进入web目录下执行,执行后编译代码默认移动到系统niucloud下的</span>
|
||||
<span class="text-[#FF9D31] mx-[3px]">public/web</span>
|
||||
<span>目录下</span>
|
||||
</div>
|
||||
<div class="ml-[40px] w-[900px] h-[42px] bg-[#282C34] rounded-[4px] mt-[10px] flex items-center">
|
||||
<span class="text-[16px] text-[#FF9D31] ml-[10px]">npm run generate</span>
|
||||
<span class="w-[58px] h-[20px] bg-[rgba(204,204,204,0.3)] ml-[auto] text-[#fff] text-[10px] flex cursor-pointer rounded-[4px] mr-[17px] items-center justify-center" @click="copyEvent('npm run build')">复制命令</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<upgrade ref="upgradeRef" @cloudbuild="handleCloudBuild"/>
|
||||
<cloud-build ref="cloudBuildRef"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue"
|
||||
import { ElMessage } from "element-plus"
|
||||
import { useClipboard } from "@vueuse/core"
|
||||
import { t } from "@/lang"
|
||||
import Upgrade from "@/app/components/upgrade/index.vue"
|
||||
import CloudBuild from "@/app/components/cloud-build/index.vue"
|
||||
|
||||
const loading = ref<Boolean>(false)
|
||||
|
||||
// 云编译调用
|
||||
const cloudBuildRef = ref<any>(null)
|
||||
const handleCloudBuild = () => {
|
||||
ElMessageBox.confirm(t("cloudBuildTips"), t("warning"), {
|
||||
confirmButtonText: t("confirm"),
|
||||
cancelButtonText: t("cancel"),
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
cloudBuildRef.value?.open()
|
||||
})
|
||||
}
|
||||
|
||||
// 复制命令
|
||||
const { copy, isSupported, copied } = useClipboard()
|
||||
|
||||
const copyEvent = (text: string) => {
|
||||
if (!isSupported.value) {
|
||||
ElMessage({
|
||||
message: t("notSupportCopy"),
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
copy(text)
|
||||
}
|
||||
|
||||
watch(copied, () => {
|
||||
if (copied.value) {
|
||||
ElMessage({
|
||||
message: t("copySuccess"),
|
||||
type: "success"
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.btn-time {
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user