update
@ -31,7 +31,7 @@ export function installAddon(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function cloudInstallAddon(params: Record<string, any>) {
|
||||
return request.post(`addon/cloudinstall/${params.addon}`, params, { timeout: 60 * 1000 })
|
||||
return request.post(`addon/cloudinstall/${params.addon}`, params)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,7 +49,7 @@ export function uninstallAddon(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function preInstallCheck(addon: string) {
|
||||
return request.get(`addon/install/check/${addon}`, { timeout: 30 * 1000 })
|
||||
return request.get(`addon/install/check/${addon}`)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,7 +75,7 @@ export function getAddonCloudInstallLog(addon: string) {
|
||||
* @returns
|
||||
*/
|
||||
export function preUninstallCheck(addon: string) {
|
||||
return request.get(`addon/uninstall/check/${addon}`, { timeout: 30 * 1000 })
|
||||
return request.get(`addon/uninstall/check/${addon}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -5,7 +5,7 @@ import request from '@/utils/request'
|
||||
* @param addon
|
||||
*/
|
||||
export function cloudBuild() {
|
||||
return request.post('niucloud/build', {}, { timeout: 0 })
|
||||
return request.post('niucloud/build', {})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -9,7 +9,7 @@ import request from '@/utils/request'
|
||||
* @returns
|
||||
*/
|
||||
export function getMemberList(params: Record<string, any>) {
|
||||
return request.get(`member/member`, {params})
|
||||
return request.get(`member/member`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +35,7 @@ export function getMemberNo() {
|
||||
* @returns
|
||||
*/
|
||||
export function addMember(params: Record<string, any>) {
|
||||
return request.post(`member/member`, params, {showSuccessMessage: true})
|
||||
return request.post(`member/member`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,10 +61,9 @@ export function getRegisterChannelType(params: Record<string, any>) {
|
||||
* @param member_id
|
||||
*/
|
||||
export function deleteMember(member_id: number) {
|
||||
return request.delete(`member/member/${member_id}`, {showSuccessMessage: true})
|
||||
return request.delete(`member/member/${member_id}`, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
|
||||
/***************************************************** 会员标签 ****************************************************/
|
||||
|
||||
/**
|
||||
@ -73,7 +72,7 @@ export function deleteMember(member_id: number) {
|
||||
* @returns
|
||||
*/
|
||||
export function getMemberLabelList(params: Record<string, any>) {
|
||||
return request.get(`member/label`, {params})
|
||||
return request.get(`member/label`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,7 +90,7 @@ export function getMemberLabelInfo(label_id: number) {
|
||||
* @returns
|
||||
*/
|
||||
export function addMemberLabel(params: Record<string, any>) {
|
||||
return request.post('member/label', params, {showSuccessMessage: true})
|
||||
return request.post('member/label', params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +98,7 @@ export function addMemberLabel(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function updateMemberLabel(params: Record<string, any>) {
|
||||
return request.put(`member/label/${params.label_id}`, params, {showSuccessMessage: true})
|
||||
return request.put(`member/label/${params.label_id}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,7 +107,7 @@ export function updateMemberLabel(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function deleteMemberLabel(label_id: number) {
|
||||
return request.delete(`member/label/${label_id}`, {showSuccessMessage: true})
|
||||
return request.delete(`member/label/${label_id}`, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +122,7 @@ export function getMemberLabelAll() {
|
||||
* @param params
|
||||
*/
|
||||
export function editMemberDetail(params: Record<string, any>) {
|
||||
return request.put(`member/member/modify/${params.member_id}/${params.field}`, params, {showSuccessMessage: true})
|
||||
return request.put(`member/member/modify/${params.member_id}/${params.field}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/***************************************************** 会员零钱 ****************************************************/
|
||||
@ -145,7 +144,16 @@ export function getChangeTypeList(change_type: string) {
|
||||
* @returns
|
||||
*/
|
||||
export function getPointList(params: Record<string, any>) {
|
||||
return request.get(`member/account/point`, {params})
|
||||
return request.get(`member/account/point`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员成长值流水
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getGrowthList(params: Record<string, any>) {
|
||||
return request.get(`member/account/growth`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,7 +162,7 @@ export function getPointList(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getBalanceList(params: Record<string, any>) {
|
||||
return request.get(`member/account/balance`, {params})
|
||||
return request.get(`member/account/balance`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,7 +171,7 @@ export function getBalanceList(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getMoneyList(params: Record<string, any>) {
|
||||
return request.get(`member/account/money`, {params})
|
||||
return request.get(`member/account/money`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,7 +180,7 @@ export function getMoneyList(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getCommissionList(params: Record<string, any>) {
|
||||
return request.get(`member/account/commission`, {params})
|
||||
return request.get(`member/account/commission`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,7 +189,7 @@ export function getCommissionList(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function adjustPoint(params: Record<string, any>) {
|
||||
return request.post(`member/account/point`, params, {showSuccessMessage: true})
|
||||
return request.post(`member/account/point`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,7 +198,7 @@ export function adjustPoint(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function adjustBalance(params: Record<string, any>) {
|
||||
return request.post(`member/account/balance`, params, {showSuccessMessage: true})
|
||||
return request.post(`member/account/balance`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/***************************************************** 会员相关设置 ****************************************************/
|
||||
@ -208,7 +216,7 @@ export function getLoginConfig() {
|
||||
* @returns
|
||||
*/
|
||||
export function setLoginConfig(params: Record<string, any>) {
|
||||
return request.post(`member/config/login`, params, {showSuccessMessage: true})
|
||||
return request.post(`member/config/login`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -224,9 +232,40 @@ export function getMemberConfig() {
|
||||
* @returns
|
||||
*/
|
||||
export function setMemberConfig(params: Record<string, any>) {
|
||||
return request.post(`member/config/member`, params, {showSuccessMessage: true})
|
||||
return request.post(`member/config/member`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 成长值规则设置
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function setGrowthRuleConfig(params: Record<string, any>) {
|
||||
return request.post(`member/config/growth_rule`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成长值规则设置
|
||||
*/
|
||||
export function getGrowthRuleConfig() {
|
||||
return request.get(`member/config/growth_rule`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 积分规则设置
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function setPointRuleConfig(params: Record<string, any>) {
|
||||
return request.post(`member/config/point_rule`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取积分规则设置
|
||||
*/
|
||||
export function getPointRuleConfig() {
|
||||
return request.get(`member/config/point_rule`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员转账方式
|
||||
@ -242,7 +281,7 @@ export function getTransfertype() {
|
||||
* @returns
|
||||
*/
|
||||
export function getCommissionSum(params: Record<string, any>) {
|
||||
return request.get(`member/account/sum_commission`, {params})
|
||||
return request.get(`member/account/sum_commission`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -251,7 +290,7 @@ export function getCommissionSum(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getPointSum(params: Record<string, any>) {
|
||||
return request.get(`member/account/sum_point`, {params})
|
||||
return request.get(`member/account/sum_point`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,7 +299,7 @@ export function getPointSum(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getBalanceSum(params: Record<string, any>) {
|
||||
return request.get(`member/account/sum_balance`, {params})
|
||||
return request.get(`member/account/sum_balance`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -293,7 +332,7 @@ export function getCashOutConfig() {
|
||||
* @returns
|
||||
*/
|
||||
export function setCashOutConfig(params: Record<string, any>) {
|
||||
return request.post(`member/config/cash_out`, params, {showSuccessMessage: true})
|
||||
return request.post(`member/config/cash_out`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,7 +341,7 @@ export function setCashOutConfig(params: Record<string, any>) {
|
||||
* @returns
|
||||
*/
|
||||
export function getCashOutList(params: Record<string, any>) {
|
||||
return request.get(`member/cash_out`, {params})
|
||||
return request.get(`member/cash_out`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -318,7 +357,7 @@ export function getCashOutDetail(id: number) {
|
||||
* @param params
|
||||
*/
|
||||
export function memberAudit(params: Record<string, any>) {
|
||||
return request.put(`member/cash_out/audit/${params.id}/${params.action}`, params, {showSuccessMessage: true})
|
||||
return request.put(`member/cash_out/audit/${params.id}/${params.action}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -326,7 +365,7 @@ export function memberAudit(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function memberTransfer(params: Record<string, any>) {
|
||||
return request.put(`member/cash_out/transfer/${params.id}`, params, {showSuccessMessage: true})
|
||||
return request.put(`member/cash_out/transfer/${params.id}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -334,7 +373,7 @@ export function memberTransfer(params: Record<string, any>) {
|
||||
* @param params
|
||||
*/
|
||||
export function editMemberStatus(params: Record<string, any>) {
|
||||
return request.put(`member/setstatus/${params.status}`, params, {showSuccessMessage: true})
|
||||
return request.put(`member/setstatus/${params.status}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -352,3 +391,134 @@ export function getCashOutStat() {
|
||||
return request.get(`member/cash_out/stat`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员权益字典
|
||||
* @returns
|
||||
*/
|
||||
export function getBenefitsDict() {
|
||||
return request.get(`member/dict/benefits`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员礼包字典
|
||||
* @returns
|
||||
*/
|
||||
export function getGiftDict() {
|
||||
return request.get(`member/dict/gift`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成长值规则字典
|
||||
* @returns
|
||||
*/
|
||||
export function getGrowthRuleDict() {
|
||||
return request.get(`member/dict/growth_rule`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取积分规则字典
|
||||
* @returns
|
||||
*/
|
||||
export function getPointRuleDict() {
|
||||
return request.get(`member/dict/point_rule`)
|
||||
}
|
||||
/***************************************************** 会员等级 ****************************************************/
|
||||
|
||||
/**
|
||||
* 获取会员等级分页列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getMemberLevelPageList(params: Record<string, any>) {
|
||||
return request.get(`member/level`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员等级列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getMemberLevelList(params: Record<string, any>) {
|
||||
return request.get(`member/level/list`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员等级详情
|
||||
* @param level_id 会员等级level_id
|
||||
* @returns
|
||||
*/
|
||||
export function getMemberLevelInfo(level_id: number) {
|
||||
return request.get(`member/level/${level_id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加会员等级
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function addMemberLevel(params: Record<string, any>) {
|
||||
return request.post('member/level', params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑会员等级
|
||||
* @param params
|
||||
*/
|
||||
export function updateMemberLevel(params: Record<string, any>) {
|
||||
return request.put(`member/level/${params.level_id}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会员等级
|
||||
* @param level_id
|
||||
* @returns
|
||||
*/
|
||||
export function deleteMemberLevel(level_id: number) {
|
||||
return request.delete(`member/level/${level_id}`, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部会员等级
|
||||
*/
|
||||
export function getMemberLevelAll() {
|
||||
return request.get(`member/level/all`);
|
||||
}
|
||||
|
||||
/***************************************************** 签到设置 ****************************************************/
|
||||
|
||||
/**
|
||||
* 获取会员权益内容
|
||||
*/
|
||||
export function getMemberBenefitsContent() {
|
||||
return request.get(`member/benefits/content`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员礼包内容
|
||||
*/
|
||||
export function getMemberGiftsContent(params: Record<string, any>) {
|
||||
return request.get(`member/gifts/content`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签到设置
|
||||
*/
|
||||
export function getSignConfig() {
|
||||
return request.get(`member/sign/config`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置签到设置
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function setSignConfig(params: Record<string, any>) {
|
||||
return request.put(`member/sign/config`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员签到记录
|
||||
*/
|
||||
export function getMemberSignList(params: Record<string, any>) {
|
||||
return request.get(`member/sign`, { params });
|
||||
}
|
||||
@ -46,7 +46,7 @@ export function getModuleVersion() {
|
||||
* @returns
|
||||
*/
|
||||
export function downloadVersion(params: Record<string, any>) {
|
||||
return request.post(`addon/download/${params.addon}`, params, { timeout: 0, showSuccessMessage: true })
|
||||
return request.post(`addon/download/${params.addon}`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
106
admin/src/app/api/poster.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取自定义海报分页列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getPosterPageList(params: Record<string, any>) {
|
||||
return request.get(`sys/poster`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义海报列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getPosterList(params: Record<string, any>) {
|
||||
return request.get(`sys/poster/list`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义海报详情
|
||||
* @param id 自定义海报id
|
||||
* @returns
|
||||
*/
|
||||
export function getPosterInfo(id: number) {
|
||||
return request.get(`sys/poster/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义海报
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function addPoster(params: Record<string, any>) {
|
||||
return request.post('sys/poster', params, {showErrorMessage: true, showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑自定义海报
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function editPoster(params: Record<string, any>) {
|
||||
return request.put(`sys/poster/${params.id}`, params, {
|
||||
showErrorMessage: true,
|
||||
showSuccessMessage: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除自定义海报
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export function deletePoster(id: number) {
|
||||
return request.delete(`sys/poster/${id}`, {showErrorMessage: true, showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改自定义海报状态
|
||||
* @param params
|
||||
*/
|
||||
export function modifyPosterStatus(params: Record<string, any>) {
|
||||
return request.put(`sys/poster/status`, params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将自定义海报修改为默认海报
|
||||
* @param params
|
||||
*/
|
||||
export function modifyPosterDefault(params: Record<string, any>) {
|
||||
return request.put(`sys/poster/default`, params, {showSuccessMessage: true})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义海报类型
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getPosterType(params: Record<string, any>) {
|
||||
return request.get(`sys/poster/type`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义海报模板
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getPosterTemplate(params: Record<string, any>) {
|
||||
return request.get(`sys/poster/template`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义海报初始化数据
|
||||
*/
|
||||
export function initPoster(params: Record<string, any>) {
|
||||
return request.get(`sys/poster/init`, {params})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义海报预览
|
||||
*/
|
||||
export function getPreviewPoster(params: Record<string, any>) {
|
||||
return request.get(`sys/poster/preview`, {params})
|
||||
}
|
||||
@ -632,14 +632,6 @@ export function getLayouts() {
|
||||
return request.get('sys/layout')
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置布局
|
||||
* @returns
|
||||
*/
|
||||
export function setLayout(key: string) {
|
||||
return request.put('sys/layout', { key }, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付待审核记录
|
||||
*/
|
||||
@ -700,3 +692,79 @@ export function getDeveloperToken() {
|
||||
export function setDeveloperToken(params: Record<string, any>) {
|
||||
return request.put(`sys/config/developer_token`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取布局设置
|
||||
* @returns
|
||||
*/
|
||||
export function getWebsiteLayout() {
|
||||
return request.get('sys/web/layout')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取布局设置
|
||||
* @returns
|
||||
*/
|
||||
export function getLayout() {
|
||||
return request.get('sys/config/layout')
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新布局设置
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function setLayout(params: Record<string, any>) {
|
||||
return request.put(`sys/config/layout`, params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/***************************************************** 报表导出 ****************************************************/
|
||||
|
||||
/**
|
||||
* 获取报表导出列表
|
||||
* @returns
|
||||
*/
|
||||
export function getExportList(params: Record<string, any>) {
|
||||
return request.get(`sys/export`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取报表导出状态列表
|
||||
* @returns
|
||||
*/
|
||||
export function getExportStatusList() {
|
||||
return request.get('sys/export/status')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取报表导出类型
|
||||
* @returns
|
||||
*/
|
||||
export function getExportKeyList() {
|
||||
return request.get('sys/export/type')
|
||||
}
|
||||
|
||||
/**
|
||||
* 报表导出
|
||||
* @returns
|
||||
*/
|
||||
export function exportData(type: string, params: Record<string, any>) {
|
||||
return request.get(`sys/export/${type}`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 报表导出数据检查
|
||||
* @returns
|
||||
*/
|
||||
export function exportDataCheck(type: string, params: Record<string, any>) {
|
||||
return request.get(`sys/export/check/${type}`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 报表删除
|
||||
* @param id
|
||||
*/
|
||||
export function deleteExport(id: number) {
|
||||
return request.delete(`sys/export/${id}`, { showSuccessMessage: true })
|
||||
}
|
||||
@ -33,6 +33,14 @@ export function getAddonDevelopCheck(key: any) {
|
||||
return request.get(`addon_develop/check/${key}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件key黑名单
|
||||
* @returns
|
||||
*/
|
||||
export function getAddonKeyBlackList(key: any) {
|
||||
return request.get('addon_develop/key/blacklist')
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加插件
|
||||
* @param key
|
||||
|
||||
@ -29,7 +29,7 @@ export function upgradeAddon(addon: string = '') {
|
||||
* 执行升级
|
||||
*/
|
||||
export function executeUpgrade() {
|
||||
return request.post('upgrade/execute', {}, { timeout: 0 })
|
||||
return request.post('upgrade/execute', {})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
60
admin/src/app/api/verify.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
/***************************************************** 核销 ****************************************************/
|
||||
|
||||
/**
|
||||
* 获取核销记录
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getVerifyRecord(params: Record<string, any>) {
|
||||
return request.get(`verify/verify/record`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取核销信息
|
||||
* @param verifyCode
|
||||
* @returns
|
||||
*/
|
||||
export function getVerifyDetail(verifyCode: string) {
|
||||
return request.get(`verify/verify/${verifyCode}`)
|
||||
}
|
||||
|
||||
/***************************************************** 核销员 ****************************************************/
|
||||
|
||||
/**
|
||||
* 获取核销员列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getVerifierList(params: Record<string, any>) {
|
||||
return request.get(`verify/verifier`, { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取核销类型列表
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function getVerifyTypeList() {
|
||||
return request.get(`verify/verifier/type`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加核销员
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function addVerifier(params: Record<string, any>) {
|
||||
return request.post('verify/verifier', params, { showSuccessMessage: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除核销员
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export function deleteVerifier(id: number) {
|
||||
return request.delete(`verify/verifier/${id}`, { showSuccessMessage: true })
|
||||
}
|
||||
@ -122,4 +122,12 @@ export function editVersion(params: Record<string, any>) {
|
||||
*/
|
||||
export function deleteVersion(id: string) {
|
||||
return request.delete(`applet/version/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询小程序是否已开通发货信息管理服务
|
||||
* @returns
|
||||
*/
|
||||
export function getIsTradeManaged() {
|
||||
return request.get('weapp/delivery/getIsTradeManaged')
|
||||
}
|
||||
BIN
admin/src/app/assets/images/diy/head/nav_style1.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
admin/src/app/assets/images/diy/head/nav_style2.jpg
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
admin/src/app/assets/images/diy/head/nav_style3.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
admin/src/app/assets/images/diy/head/nav_style4.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
admin/src/app/assets/images/diy/head/nav_style5.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
admin/src/app/assets/images/diy/head/nav_style6.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
admin/src/app/assets/images/layout_bussiness.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
admin/src/app/assets/images/layout_darkside.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
admin/src/app/assets/images/layout_default.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
admin/src/app/assets/images/layout_profession.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 3.2 KiB |
@ -103,18 +103,30 @@
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<cloud-build ref="cloudBuildRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, h, watch} from 'vue'
|
||||
import { ref, h, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getUpgradeContent, getUpgradeTask, upgradeAddon, executeUpgrade, preUpgradeCheck, clearUpgradeTask } 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 { AnyObject } from '@/types/global'
|
||||
import CloudBuild from '@/app/components/cloud-build/index.vue'
|
||||
import { ElNotification, ElMessage, ElMessageBox } from "element-plus"
|
||||
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import Storage from '@/utils/storage'
|
||||
|
||||
const showDialog = ref<boolean>(false)
|
||||
const upgradeContent = ref<null | AnyObject>(null)
|
||||
@ -125,6 +137,7 @@ const uploading = ref(false)
|
||||
const terminalRef = ref(null)
|
||||
const emits = defineEmits(['complete'])
|
||||
const cloudBuildRef = ref(null)
|
||||
const upgradeTipsShowDialog = ref<boolean>(false)
|
||||
|
||||
let upgradeLog = []
|
||||
/**
|
||||
@ -229,16 +242,20 @@ const open = (addonKey: string = '') => {
|
||||
if (upgradeTask.value) {
|
||||
ElMessage({ message: '已有正在执行中的升级任务', type: 'error' })
|
||||
showDialog.value = true
|
||||
return
|
||||
} else {
|
||||
getUpgradeContent(addonKey).then(({ data }) => {
|
||||
upgradeContent.value = data
|
||||
if (!data.version_list.length) {
|
||||
ElMessage({ message: '已经是最新版本了', type: 'error' })
|
||||
return
|
||||
}
|
||||
if (Storage.get('upgradeTipsLock')) {
|
||||
showDialog.value = true
|
||||
} else {
|
||||
upgradeTipsShowDialog.value = true
|
||||
}
|
||||
}).catch()
|
||||
}
|
||||
getUpgradeContent(addonKey).then(({ data }) => {
|
||||
upgradeContent.value = data
|
||||
if (!data.version_list.length) {
|
||||
ElMessage({ message: '已经是最新版本了', type: 'error' })
|
||||
return
|
||||
}
|
||||
showDialog.value = true
|
||||
}).catch()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -309,6 +326,12 @@ const handleCloudBuild = () => {
|
||||
cloudBuildRef.value?.open()
|
||||
}
|
||||
|
||||
const upgradeTipsConfirm = (isLock: boolean = false) => {
|
||||
isLock && Storage.set({ key: 'upgradeTipsLock', data: isLock })
|
||||
upgradeTipsShowDialog.value = false
|
||||
!isLock && (showDialog.value = true)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"menuDeleteTips": "删除菜单会删除当前菜单以及该菜单下所有子菜单,是否确认删除?",
|
||||
"initializeMenu":"重置菜单",
|
||||
"initializeMenuTipsOne":"重置菜单会将应用或插件的dict目录下的菜单配置文件中,菜单配置更新到数据库一般用做开发者修改了dict菜单配置文件后,同步到数据库操作。",
|
||||
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常不允诛进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
|
||||
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常允许进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
|
||||
"addMenu": "添加菜单",
|
||||
"updateMenu": "编辑菜单",
|
||||
"routePath": "路由路径",
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
"addMenu": "添加菜单",
|
||||
"initializeMenu":"重置菜单",
|
||||
"initializeMenuTipsOne":"重置菜单会将应用或插件的dict目录下的菜单配置文件中,菜单配置更新到数据库一般用做开发者修改了dict菜单配置文件后,同步到数据库操作。",
|
||||
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常不允诛进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
|
||||
"initializeMenuTipsTwo":"如果用户手动调整过以下菜单项,通常允许进行本项操作,操作会重置为原始菜单。 请谨慎使用!",
|
||||
"updateMenu": "编辑菜单",
|
||||
"routePath": "路由路径",
|
||||
"viewPath": "组件路径",
|
||||
@ -30,4 +30,4 @@
|
||||
"addon":"选择应用",
|
||||
"system":"系统菜单",
|
||||
"application":"应用菜单"
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,5 +38,8 @@
|
||||
"toSetting": "去配置",
|
||||
"cloudRelease": "一键云端发布",
|
||||
"localRelease": "本地发布",
|
||||
"localInsertTips": "请先将uni-app编译成微信小程序,然后使用微信开发者工具进行上传"
|
||||
"localInsertTips": "请先将uni-app编译成微信小程序,然后使用微信开发者工具进行上传",
|
||||
"uploadSuccessTips": "小程序上传成功后还需到<a href='https://mp.weixin.qq.com/' target='_blank' class='text-primary'>微信公众平台</a>提交审核,审核通过后发布才算正式上线。",
|
||||
"knownToKnow": "我已知晓,不需要再次提示",
|
||||
"siteAuthTips": "上传代码需先绑定授权码,请联系平台管理员进行绑定"
|
||||
}
|
||||
|
||||
@ -17,6 +17,6 @@
|
||||
"weappAccessFlow": "接入流程",
|
||||
"subscribeMessage": "订阅消息",
|
||||
"weappRelease": "版本管理",
|
||||
"batchAcquisition": "一键获取"
|
||||
|
||||
}
|
||||
"batchAcquisition": "一键获取",
|
||||
"addon": "所属应用"
|
||||
}
|
||||
|
||||
@ -21,5 +21,6 @@
|
||||
"wechatAccessFlow": "接入流程",
|
||||
"customMenu": "自定义菜单",
|
||||
"wechatTemplate": "模板消息",
|
||||
"reply": "自动回复"
|
||||
"reply": "自动回复",
|
||||
"addon": "所属应用"
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"templatePagePlaceholder": "选择模板",
|
||||
"templatePageEmpty": "无",
|
||||
"changeTemplatePageTips":"切换模板后,当前页面内容将被替换且不被保存,请谨慎操作",
|
||||
"changeTemplatePageTips": "切换模板后,当前页面内容将被替换且不被保存,请谨慎操作",
|
||||
"developTitle": "开发环境配置",
|
||||
"wapDomain": "wap域名(WAP_DOMAIN)",
|
||||
"wapDomainPlaceholder": "请输入wap域名",
|
||||
@ -10,8 +10,15 @@
|
||||
"tabEditStyle": "样式",
|
||||
"pageStyle": "页面样式",
|
||||
"pageContent": "页面内容",
|
||||
"pageName": "页面名称",
|
||||
"pageNamePlaceholder": "请输入页面名称",
|
||||
"statusBarContent": "导航栏内容",
|
||||
"statusBarStyle": "导航栏样式",
|
||||
"bottomNavContent": "底部导航内容",
|
||||
"diyPageTitle": "页面名称",
|
||||
"diyPageTitlePlaceholder": "请输入页面名称",
|
||||
"pageTitleTips": "页面名称用于后台显示",
|
||||
"diyTitle": "页面标题",
|
||||
"diyTitlePlaceholder": "请输入页面标题",
|
||||
"titleTips": "页面标题用于前台显示",
|
||||
"pageBgColor": "页面颜色",
|
||||
"bgUrl": "背景图片",
|
||||
"bgHeightScale": "高度比例",
|
||||
@ -97,6 +104,8 @@
|
||||
"titleStyle": "标题样式",
|
||||
"selectStyle": "风格选择",
|
||||
"styleLabel": "风格",
|
||||
"styleShowTips": "风格 1 2 3 5 6,仅在小程序中展示",
|
||||
"topStatusBarBgColorTips": "当导航栏样式为风格5且页面滚动时,背景颜色会跟随顶部颜色的设置而改变",
|
||||
"titleContent": "标题内容",
|
||||
"title": "标题名称",
|
||||
"titlePlaceholder": "请输入标题",
|
||||
@ -142,10 +151,14 @@
|
||||
"addAddon": "添加应用",
|
||||
"show": "显示",
|
||||
"hidden": "隐藏",
|
||||
"goodsCategoryTitle":"商品分类",
|
||||
"customGoods":"手动选择",
|
||||
"goodsNum":"商品数量",
|
||||
"selectCategory":"选择分类",
|
||||
"goodsCategoryTitle": "商品分类",
|
||||
"customGoods": "手动选择",
|
||||
"goodsNum": "商品数量",
|
||||
"selectCategory": "选择分类",
|
||||
"isBecomeFenxiao": "成为分销商",
|
||||
"isBecomeFenxiaoDesc": "是否展示会员成为分销商的按钮",
|
||||
"are": "是",
|
||||
"no": "否",
|
||||
"categoryName": "分类名称",
|
||||
"categoryImage": "分类图片",
|
||||
"selectSource": "选择数据源",
|
||||
@ -205,7 +218,6 @@
|
||||
"carouselSearchAddTabItem": "添加一个选项卡",
|
||||
"selectSourcesDiyPage": "选择微页面",
|
||||
"selectDiyPagePlaceholder": "请选择微页面",
|
||||
"diyPageTitle": "页面名称",
|
||||
"diyPageTypeName": "页面类型",
|
||||
"diyPageForAddon": "所属应用",
|
||||
"carouselSearchSwiperSet": "轮播图设置",
|
||||
@ -236,5 +248,15 @@
|
||||
"floatBtnImageSet": "图片设置",
|
||||
"floatBtnImageSize": "图片大小",
|
||||
"floatBtnAroundRadius": "图片圆角",
|
||||
"floatBtnImageSuggest": "建议上传正方形图片"
|
||||
}
|
||||
"floatBtnImageSuggest": "建议上传正方形图片",
|
||||
"topStatusBarImg": "图片",
|
||||
"topStatusBarNav": "导航栏",
|
||||
"topStatusBarNavTips": "此处控制当前页面导航栏是否显示",
|
||||
"topStatusBarImgTips": "宽度自适应(最大150px),高度28px",
|
||||
"topStatusBarIsTransparent": "顶部透明",
|
||||
"topStatusBarTextColor": "标题颜色",
|
||||
"topStatusBarBgColor": "顶部颜色",
|
||||
"topStatusBarSearchName": "搜索内容",
|
||||
"topStatusBarSearchNamePlaceholder": "请输入搜索关键词",
|
||||
"settingTips": "点击查看如何配置"
|
||||
}
|
||||
|
||||
34
admin/src/app/lang/zh-cn/marketing.sign_config.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"signSet": "签到设置",
|
||||
"signList": "签到记录",
|
||||
"isUse": "是否启用",
|
||||
"signPeriod": "签到周期",
|
||||
"signPeriodTip": "请输入签到周期",
|
||||
"calendarSign": "日历签到",
|
||||
"periodSign": "周期签到",
|
||||
"daySignAward": "日签奖励",
|
||||
"continueSignAward": "连签奖励",
|
||||
"calendarSignTip": "用户根据日期进行打卡,连续签到一定天数可即可获得连签奖励。",
|
||||
"periodSignTip": "用户在规定的周期内完成签到可以获得奖励;一个周期结束后将进入下一个循环周期。",
|
||||
"daySignAwardTip": "用户每日签到可以获得的奖励",
|
||||
"continueSignAwardTipTop": "超过固定周期天数的奖励将不向用户展示和发放",
|
||||
"continueSignAwardTipBottom": "若用户已经达到新增的连签条件,则不补发连奖励(例:新增连签3天获得赠品,若已有用户连签超过3天,则不补发赠品)",
|
||||
"set": "设置",
|
||||
"modify": "修改",
|
||||
"add": "+新增连签奖励",
|
||||
"signRule": "签到规则",
|
||||
"daySignTitle": "设置日签奖励",
|
||||
"continueSignTitle": "设置连续奖励",
|
||||
"ruleExplain": "规则说明",
|
||||
"ruleExplainTip": "请输入规则说明",
|
||||
"ruleExplainDefault": "1.每日签到可以获得日签奖励,连续签到可以获得连签奖励;\n2.每日最多可签到1次,断签则会重新计算连签天数;\n3.活动以及奖励最终解释权归商家所有。",
|
||||
"useDefaultExplain": "使用默认说明",
|
||||
"continueSign": "连续签到天数",
|
||||
"receiveLimit": "领取限制",
|
||||
"noLimit": "不限制",
|
||||
"everyOneLimit": "每人限领",
|
||||
"time": "次",
|
||||
"day": "天",
|
||||
"continueSignPlaceholder":"请输入连续签到天数",
|
||||
"receiveNumPlaceholder":"请输入限领次数"
|
||||
}
|
||||
15
admin/src/app/lang/zh-cn/marketing.sign_list.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"memberId":"会员编号",
|
||||
"memberInfoPlaceholder":"请输入会员信息",
|
||||
"memberInfo":"会员信息",
|
||||
"mobile":"手机号码",
|
||||
"nickName":"会员昵称",
|
||||
"headimg":"会员头像",
|
||||
"createTime":"签到时间",
|
||||
"days":"连续签到",
|
||||
"day":"天",
|
||||
"dayAward":"日签奖励",
|
||||
"continueAward":"连签奖励",
|
||||
"startDate":"开始时间",
|
||||
"endDate":"结束时间"
|
||||
}
|
||||
31
admin/src/app/lang/zh-cn/marketing.verifier.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"hotelVerify": "订单核销",
|
||||
"verifyRecord": "核销记录",
|
||||
"verifyTime": "核销时间",
|
||||
"orderNo": "订单编号",
|
||||
"orderNoPlaceholder": "请输入订单编号",
|
||||
"OrderInfo": "订单信息",
|
||||
"buyerInfo": "购买人信息",
|
||||
"verifyCode": "核销码",
|
||||
"verifyCodePlaceholder": "请输入核销码",
|
||||
"verifyer": "核销人",
|
||||
"startDate": "开始时间",
|
||||
"endDate": "结束时间",
|
||||
"mobile": "联系方式",
|
||||
"searchValueEmptyTips": "请输入搜索内容",
|
||||
"verify": "核销",
|
||||
"buyInfo": "预订信息",
|
||||
"orderRefunding": "该订单正在维权中不能进行核销",
|
||||
"verifyTips": "是否要核销该订单?",
|
||||
"toOrder": "查看订单",
|
||||
"verifyType": "核销类型",
|
||||
"verifyTypePlaceholder": "请选择核销类型",
|
||||
"verifier": "核销员",
|
||||
"createTime":"添加时间",
|
||||
"addVerifier":"添加核销员",
|
||||
"verifierDeleteTips":"确定要删除该核销员吗?",
|
||||
"memberInfo": "会员信息",
|
||||
"memberIdPlaceholder": "请选择会员",
|
||||
"member": "会员",
|
||||
"searchPlaceholder": "请输入会员昵称搜索"
|
||||
}
|
||||
31
admin/src/app/lang/zh-cn/marketing.verify.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"hotelVerify": "订单核销",
|
||||
"verifyRecord": "核销记录",
|
||||
"verifyTime": "核销时间",
|
||||
"orderNo": "订单编号",
|
||||
"orderNoPlaceholder": "请输入订单编号",
|
||||
"verifyInfo": "核销信息",
|
||||
"buyerInfo": "购买人信息",
|
||||
"verifyCode": "核销码",
|
||||
"verifyCodePlaceholder": "请输入核销码",
|
||||
"verifyer": "核销人",
|
||||
"startDate": "开始时间",
|
||||
"endDate": "结束时间",
|
||||
"mobile": "联系方式",
|
||||
"searchValueEmptyTips": "请输入搜索内容",
|
||||
"verify": "核销",
|
||||
"buyInfo": "预订信息",
|
||||
"orderRefunding": "该订单正在维权中不能进行核销",
|
||||
"verifyTips": "是否要核销该订单?",
|
||||
"toOrder": "查看订单",
|
||||
"verifyType": "核销类型",
|
||||
"verifyTypePlaceholder": "请选择核销类型",
|
||||
"verifier": "核销员",
|
||||
"createTime":"添加时间",
|
||||
"addVerifier":"添加核销员",
|
||||
"verifierDeleteTips":"确定要删除该核销员吗?",
|
||||
"memberInfo": "会员信息",
|
||||
"memberIdPlaceholder": "请选择会员",
|
||||
"member": "会员",
|
||||
"searchPlaceholder": "请输入会员昵称搜索"
|
||||
}
|
||||
31
admin/src/app/lang/zh-cn/member.growth.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"accountData":"变更数值",
|
||||
"accountSum":"变更后成长值",
|
||||
"fromType":"来源",
|
||||
"memberId":"会员编号",
|
||||
"pointInfo":"积分变动详情",
|
||||
"memo":"备注",
|
||||
"mobile":"手机号码",
|
||||
"nickName":"会员信息",
|
||||
"headimg":"会员头像",
|
||||
"createTime":"发生时间",
|
||||
"startDate":"开始时间",
|
||||
"endDate":"结束时间",
|
||||
"searchMember":"昵称/手机号",
|
||||
"searchMemberPlaceholder":"请输入会员昵称/手机号码",
|
||||
"memberIdPlaceholder":"请输入会员id",
|
||||
"accountTypePlaceholder":"请输入账户类型",
|
||||
"accountDataPlaceholder":"请输入账户数据",
|
||||
"fromTypePlaceholder":"请输入来源类型",
|
||||
"relatedIdPlaceholder":"请输入关联Id",
|
||||
"createTimePlaceholder":"请输入创建时间",
|
||||
"memoPlaceholder":"请输入备注信息",
|
||||
"addMemberAccountLog":"添加会员账单表",
|
||||
"updateMemberAccountLog":"编辑会员账单表",
|
||||
"member_account_logDeleteTips":"确定要删除该会员账单表吗?",
|
||||
"memberInfo":"会员信息",
|
||||
"memberInfoPlaceholder":"请输入会员编号/昵称/手机号",
|
||||
"memberLevel": "会员等级",
|
||||
"growthRule": "成长值规则",
|
||||
"growthDetail": "成长值明细"
|
||||
}
|
||||
14
admin/src/app/lang/zh-cn/member.level.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"levelName":"等级名称",
|
||||
"memberNumber":"会员数量",
|
||||
"memberLabelDeleteTips":"确定要删除该会员登记吗?",
|
||||
"addMemberLevel": "添加会员等级",
|
||||
"levelNamePlaceholder": "请输入等级名称",
|
||||
"memberLevelDeleteTips": "确定要删除该等级吗?",
|
||||
"growth": "等级成长值",
|
||||
"levelBenefits": "等级权益",
|
||||
"levelGifts": "等级礼包",
|
||||
"memberLevel": "会员等级",
|
||||
"growthRule": "成长值规则",
|
||||
"growthDetail": "成长值明细"
|
||||
}
|
||||
20
admin/src/app/lang/zh-cn/member.level_edit.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"basicInfo": "基础信息",
|
||||
"levelName":"等级名称",
|
||||
"memberNumber":"会员数量",
|
||||
"memberLabelDeleteTips":"确定要删除该会员登记吗?",
|
||||
"addMemberLevel": "添加会员等级",
|
||||
"levelNamePlaceholder": "请输入等级名称",
|
||||
"levelBenefits": "等级权益",
|
||||
"levelGift": "等级礼包",
|
||||
"benefits": "权益",
|
||||
"gift": "礼包",
|
||||
"growthPlaceholder": "请输入成长值",
|
||||
"growth": "成长值",
|
||||
"remark": "等级描述",
|
||||
"remarkPlaceholder": "请输入等级描述",
|
||||
"growthFormatError": "成长值只能为整数",
|
||||
"growthNeedLt": "成长值需小于",
|
||||
"growthNeedGt": "成长值需大于",
|
||||
"growthTips": "升级到该等级需达到的最低成长值"
|
||||
}
|
||||
@ -49,5 +49,6 @@
|
||||
"memberInfo":"会员信息",
|
||||
"memberInfoPlaceholder":"请输入会员编号/昵称/手机号",
|
||||
"lock": "锁定",
|
||||
"normal": "正常"
|
||||
}
|
||||
"normal": "正常",
|
||||
"memberLevel": "会员等级"
|
||||
}
|
||||
|
||||
@ -21,8 +21,8 @@
|
||||
"sexPlaceholder": "请选择性别",
|
||||
"headimg": "会员头像",
|
||||
"wxUnionid": "微信unionid",
|
||||
"weappOpenid": "微信公众号openid",
|
||||
"wxOpenid": "微信小程序openid",
|
||||
"weappOpenid": "微信小程序openid",
|
||||
"wxOpenid": "微信公众号openid",
|
||||
"memberLabel": "会员标签",
|
||||
"memberLabelPlaceholder": "请选择会员标签",
|
||||
"nickNamePlaceholder": "请输入会员名称",
|
||||
@ -47,5 +47,9 @@
|
||||
"money":"可提现余额",
|
||||
"adjustPoint":"调整积分",
|
||||
"commission":"佣金",
|
||||
"memberNull":"未读取到会员详情信息"
|
||||
}
|
||||
"memberNull":"未读取到会员详情信息",
|
||||
"memberLevel": "会员等级",
|
||||
"memberLevelUpdate": "修改等级至",
|
||||
"memberLevelUpdateTips": "该操作只会修改会员等级不会发放等级礼包",
|
||||
"memberLevelPlaceholder": "请选择会员等级"
|
||||
}
|
||||
|
||||
51
admin/src/app/lang/zh-cn/poster.edit.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"posterName": "海报名称",
|
||||
"posterNamePlaceholder": "请输入海报名称",
|
||||
"bgType": "背景类型",
|
||||
"bgUrl": "背景图",
|
||||
"bgColor": "背景色",
|
||||
"bgUrlTips": "建议图片尺寸:720*1280px",
|
||||
"statusLabel": "启用状态",
|
||||
"statusTips": "此处控制海报的启用状态",
|
||||
"textSet": "文本设置",
|
||||
"textLabel": "文本内容",
|
||||
"textPlaceholder": "请输入文本内容",
|
||||
"templatePosterPlaceholder": "选择模板",
|
||||
"templatePosterEmpty": "无",
|
||||
"changeTemplatePosterTips": "切换模板后,当前页面内容将被替换且不被保存,请谨慎操作",
|
||||
"posterSet": "海报设置",
|
||||
"diyPosterValueEmptyTips": "请编辑海报内容",
|
||||
"componentStyleTitle": "组件样式",
|
||||
"pageContent": "页面内容",
|
||||
"zIndex": "层级",
|
||||
"coordinate": "坐标(x,y)",
|
||||
"coordinateTips": "x为横向坐标,y为纵向坐标",
|
||||
"leavePageTitleTips": "确定离开此页面?",
|
||||
"leavePageContentTips": "系统可能不会保存您所做的更改。",
|
||||
"decorating": "正在装修",
|
||||
"preview": "预览",
|
||||
"previewDialogTitle": "预览海报",
|
||||
"moveUpComponentZIndex": "上移一层",
|
||||
"moveDownComponentZIndex": "下移一层",
|
||||
"copyComponent": "复制",
|
||||
"delComponent": "删除",
|
||||
"resetComponent": "重置",
|
||||
"delComponentTips": "确认要删除当前组件吗?",
|
||||
"notCopy": "无法复制",
|
||||
"componentCanOnlyAdd": "组件只能添加",
|
||||
"piece": "个",
|
||||
"componentNotMoved": "该组件禁止移动",
|
||||
"resetComponentTips": "确认要重置组件默认数据吗?",
|
||||
"imageUrlTip": "请上传图片",
|
||||
"textFontSize": "文字大小",
|
||||
"textFontWeight": "文字粗细",
|
||||
"fontWeightBold": "加粗",
|
||||
"fontWeightNormal": "常规",
|
||||
"textColor": "文字颜色",
|
||||
"width": "宽度",
|
||||
"drawType": "类型",
|
||||
"polygon": "多边形",
|
||||
"angle": "旋转角度",
|
||||
"nickName": "昵称",
|
||||
"needLoginTips": "前台需要登录才会展示"
|
||||
}
|
||||
18
admin/src/app/lang/zh-cn/poster.list.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"posterName": "海报名称",
|
||||
"posterNamePlaceholder": "请输入海报名称",
|
||||
"posterType": "海报类型",
|
||||
"posterTypePlaceholder": "请选择海报类型",
|
||||
"isDefault": "是否默认",
|
||||
"defaultPoster": "默认海报",
|
||||
"noDefault": "否",
|
||||
"addPosterTitle": "添加海报",
|
||||
"previewDialogTitle": "预览海报",
|
||||
"title": "页面名称",
|
||||
"status": "状态",
|
||||
"updateTime": "更新时间",
|
||||
"all": "全部",
|
||||
"diyPosterDeleteTips": "确定要删除该自定义海报吗?",
|
||||
"preview": "预览",
|
||||
"modifyDefault": "设为默认"
|
||||
}
|
||||
15
admin/src/app/lang/zh-cn/setting.export.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"exportKey": "数据类型",
|
||||
"exportKeyPlaceholder": "请输入主题关键字",
|
||||
"exportStatus": "导出状态",
|
||||
"exportStatusPlaceholder": "请选择导出状态",
|
||||
"createTime": "导出时间",
|
||||
"id": "编号",
|
||||
"exportNum": "导出数据数量",
|
||||
"filePath": "文件存储路径",
|
||||
"fileSize": "文件大小",
|
||||
"download": "下载",
|
||||
"exportDeleteTips":"确定要删除该导出报表吗?",
|
||||
"startDate": "开始时间",
|
||||
"endDate": "结束时间"
|
||||
}
|
||||
5
admin/src/app/lang/zh-cn/setting.growth_rule.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"memberLevel": "会员等级",
|
||||
"growthRule": "成长值规则",
|
||||
"growthDetail": "成长值明细"
|
||||
}
|
||||
7
admin/src/app/lang/zh-cn/setting.layout.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"app": "应用",
|
||||
"setting": "设置",
|
||||
"selectLayout": "选择布局",
|
||||
"emptyData": "还没有安装应用",
|
||||
"manyApp": "多应用"
|
||||
}
|
||||
@ -1,4 +1,11 @@
|
||||
{
|
||||
"clickTutorial": "查看教程",
|
||||
"clickSecretKey": "获取密钥"
|
||||
"clickSecretKey": "获取密钥",
|
||||
"isOpen": "定位开关",
|
||||
"validTime": "定位有效期",
|
||||
"minutes": "分钟",
|
||||
"validTimeTips": "过期后将重新获取定位信息,0为不过期",
|
||||
"validTimePlaceholder": "请输入定位有效期",
|
||||
"validTimeFormatTips": "格式输入错误",
|
||||
"validTimeNotZeroTips": "定位有效期不能小于0"
|
||||
}
|
||||
@ -14,7 +14,6 @@
|
||||
"groupIdPlaceholder":"请输入套餐",
|
||||
"addSite":"添加站点",
|
||||
"updateSite":"编辑站点",
|
||||
|
||||
"expireTimePlaceholder":"请选择到期时间",
|
||||
"desc": "网站简介",
|
||||
"province": "省",
|
||||
|
||||
@ -49,5 +49,7 @@
|
||||
"siteDomainPlaceholder": "请输入站点域名",
|
||||
"siteDomainTips": "站点域名的配置是针对站点的wap和web端",
|
||||
"siteDomainTipsTwo": "需要将域名配置到您的服务器,同时域名需要解析您的服务器才可生效",
|
||||
"toSite": "访问站点"
|
||||
"siteDomainTipsThree": "站点域名不需要加http或者https,末尾不需要加/",
|
||||
"toSite": "访问站点",
|
||||
"noPermission": "您没有该站点的管理权限"
|
||||
}
|
||||
|
||||
@ -61,12 +61,12 @@
|
||||
<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="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width" clearable size="large" />
|
||||
<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="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||
<el-input v-model.trim="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="main-container w-full bg-white" v-loading="loading">
|
||||
<div class="main-container w-full " v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">应用管理</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="appList.length">
|
||||
<div v-for="(item, index) in appList" :key="index + 'b'">
|
||||
<div class="relative app-item cursor-pointer px-4 mr-4 mt-[20px] bg-[#f7f7f7] border-[1px] hover:border-primary">
|
||||
<div class="relative bg-page cursor-pointer px-4 mr-4 mt-[20px] border-br-light border-[1px] hover:border-primary">
|
||||
<div @click="toLink(item.key)" class="flex py-5 items-center">
|
||||
<div class="flex justify-center items-center">
|
||||
<el-image class="w-[40px] h-[40px]" :src="img(item.icon)" fit="contain">
|
||||
@ -18,7 +18,7 @@
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between text-left w-[190px]">
|
||||
<p class="app-text w-[190px] text-[17px] text-[#222] pl-3">{{ item.title }}</p>
|
||||
<p class="app-text w-[190px] text-[17px] pl-3">{{ item.title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="flex justify-between items-center h-[32px]">
|
||||
<span class="text-[16px] text-[#222] font-600">{{ t('localAppText') }}</span>
|
||||
<div class="w-[247px]">
|
||||
<el-input :placeholder="t('search')" v-model="searchName" @keyup.enter="query">
|
||||
<el-input :placeholder="t('search')" v-model.trim="searchName" @keyup.enter="query">
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon cursor-pointer" size="14px" @click="query">
|
||||
<search />
|
||||
@ -139,12 +139,12 @@
|
||||
<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="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width" clearable size="large" />
|
||||
<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="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||
<el-input v-model.trim="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width" size="large" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
@ -24,16 +24,15 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('userRealName')" prop="real_name">
|
||||
<el-input v-model="formData.real_name" :placeholder="t('userRealNamePlaceholder')" clearable class="input-width" maxlength="10" show-word-limit />
|
||||
<el-input v-model="formData.real_name" :placeholder="t('userRealNamePlaceholder')" :readonly="real_name_input" @click="real_name_input = false" @blur="real_name_input = true" clearable class="input-width" maxlength="10" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="!formData.uid">
|
||||
<el-form-item :label="t('password')" prop="password">
|
||||
<el-input v-model="formData.password" :placeholder="t('passwordPlaceholder')" type="password" :show-password="true" clearable class="input-width" />
|
||||
<el-input v-model="formData.password" :placeholder="t('passwordPlaceholder')" :readonly="password_input" @click="password_input = false" @blur="password_input = true" type="password" :show-password="true" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('confirmPassword')" prop="confirm_password">
|
||||
<el-input v-model="formData.confirm_password" :placeholder="t('confirmPasswordPlaceholder')" type="password" :show-password="true" clearable class="input-width" />
|
||||
<el-input v-model="formData.confirm_password" :placeholder="t('confirmPasswordPlaceholder')" :readonly="confirm_password_input" @click="confirm_password_input = false" @blur="confirm_password_input = true" type="password" :show-password="true" clearable class="input-width" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
@ -85,7 +84,9 @@ const getUserList = () => {
|
||||
}).catch()
|
||||
}
|
||||
getUserList()
|
||||
|
||||
const real_name_input = ref(true)
|
||||
const password_input = ref(true)
|
||||
const confirm_password_input = ref(true)
|
||||
const needAddUserInfo = computed(() => {
|
||||
if (formData.uid || !uid.value || typeof uid.value == 'string') {
|
||||
return true
|
||||
|
||||
@ -92,11 +92,9 @@ const refreshMenu = () => {
|
||||
// type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
menusTableData.loading = true
|
||||
menuRefresh({}).then(res => {
|
||||
menusTableData.loading = false
|
||||
location.reload()
|
||||
}).catch(() => {
|
||||
menusTableData.loading = false
|
||||
})
|
||||
}).catch(()=>{})
|
||||
|
||||
|
||||
@ -138,12 +138,9 @@ const refreshMenu = () => {
|
||||
// type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
menusTableData.loading = true
|
||||
menuRefresh({}).then(res => {
|
||||
menusTableData.loading = false
|
||||
}).catch(() => {
|
||||
menusTableData.loading = false
|
||||
})
|
||||
location.reload()
|
||||
}).catch(() => {})
|
||||
}).catch(()=>{})
|
||||
|
||||
}
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
</el-form>
|
||||
<el-button type="primary" class="w-[100px] self-start" @click="addEvent">{{ t('addUser') }}</el-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-table :data="userTableData.data" size="large" v-loading="userTableData.loading">
|
||||
<template #empty>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<!-- <div class="w-full p-5 bg-white ">
|
||||
<!-- <div class="w-full p-5 ">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-[16px]">{{ t('aliappAccessFlow') }}</span>
|
||||
</div>
|
||||
@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="w-full p-5 bg-white">
|
||||
<div class="w-full p-5 bg-body">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ t('title') }}</span>
|
||||
</div>
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
</div>
|
||||
<el-form :model="formData" label-width="150px" ref="formRef" class="page-form">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-form-item :label="t('preview')" prop="weapp_name">
|
||||
<img class="w-[500px]" src="@/app/assets/images/channel/preview.png" alt="">
|
||||
</el-form-item>
|
||||
<!-- <el-form-item :label="t('preview')" prop="weapp_name">-->
|
||||
<!-- <img class="w-[500px]" src="@/app/assets/images/channel/preview.png" alt="">-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<el-form-item :label="t('isOpen')">
|
||||
<el-switch v-model="formData.is_open"/>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full p-5 bg-white">
|
||||
<div class="w-full p-5 bg-body">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ t('title') }}</span>
|
||||
</div>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<el-tab-pane :label="t('subscribeMessage')" name="/channel/weapp/message" />
|
||||
<el-tab-pane :label="t('weappRelease')" name="/channel/weapp/code" />
|
||||
</el-tabs>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
|
||||
<div class="mt-[50px]">
|
||||
<el-button type="primary" @click="insert" :loading="uploading" :disabled="weappTableData.loading">{{ t('cloudRelease') }}</el-button>
|
||||
<el-button @click="localInsert" :disabled="weappTableData.loading">{{ t('localRelease') }}</el-button>
|
||||
@ -70,6 +70,15 @@
|
||||
{{ failReason }}
|
||||
</el-scrollbar>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="uploadSuccessShowDialog" :title="t('warning')" width="500px" draggable>
|
||||
<span v-html="t('uploadSuccessTips')"></span>
|
||||
<template #footer>
|
||||
<div class="flex justify-end">
|
||||
<el-button @click="knownToKnow" type="primary">{{ t('knownToKnow') }}</el-button>
|
||||
<el-button @click="uploadSuccessShowDialog = false" type="primary" plain>{{ t('confirm') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -79,13 +88,16 @@ import { setWeappVersion, getWeappPreview, getWeappVersionList, getWeappUploadLo
|
||||
import { t } from '@/lang'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getAuthinfo } from '@/app/api/module'
|
||||
import { getAppType } from '@/utils/common'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { AnyObject } from '@/types/global'
|
||||
import Storage from '@/utils/storage'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const pageName = route.meta.title
|
||||
const dialogVisible = ref(false)
|
||||
const loading = ref(true)
|
||||
const weappTableData:{
|
||||
page: number,
|
||||
limit: number,
|
||||
@ -105,14 +117,17 @@ const form = ref({
|
||||
path: '',
|
||||
content: ''
|
||||
})
|
||||
|
||||
const uploadSuccessShowDialog = ref(false)
|
||||
const authCode = ref('')
|
||||
|
||||
getAuthinfo().then(res => {
|
||||
if (res.data.data && res.data.data.auth_code) {
|
||||
authCode.value = res.data.data.auth_code
|
||||
getWeappPreviewImage()
|
||||
}
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
const weappConfig = ref<{
|
||||
@ -211,6 +226,7 @@ const getWeappUploadLogFn = (key: string) => {
|
||||
if (last.code == 1 && last.percent == 100) {
|
||||
getWeappVersionListFn()
|
||||
getWeappPreviewImage()
|
||||
!Storage.get('weappUploadTipsLock') && (uploadSuccessShowDialog.value = true)
|
||||
return
|
||||
}
|
||||
setTimeout(() => {
|
||||
@ -221,21 +237,25 @@ const getWeappUploadLogFn = (key: string) => {
|
||||
}
|
||||
|
||||
const authElMessageBox = () => {
|
||||
ElMessageBox.confirm(
|
||||
t('authTips'),
|
||||
t('warning'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: t('toBind'),
|
||||
cancelButtonText: t('toNiucloud')
|
||||
}
|
||||
).then(() => {
|
||||
router.push({ path: '/app/authorize' })
|
||||
}).catch((action: string) => {
|
||||
if (action === 'cancel') {
|
||||
window.open('https://www.niucloud.com/app')
|
||||
}
|
||||
})
|
||||
if (getAppType() == 'admin') {
|
||||
ElMessageBox.confirm(
|
||||
t('authTips'),
|
||||
t('warning'),
|
||||
{
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: t('toBind'),
|
||||
cancelButtonText: t('toNiucloud')
|
||||
}
|
||||
).then(() => {
|
||||
router.push({ path: '/app/authorize' })
|
||||
}).catch((action: string) => {
|
||||
if (action === 'cancel') {
|
||||
window.open('https://www.niucloud.com/app')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ElMessageBox.alert(t('siteAuthTips'), t('warning'))
|
||||
}
|
||||
}
|
||||
|
||||
const configElMessageBox = () => {
|
||||
@ -258,6 +278,11 @@ const handleFailReason = (data: any) => {
|
||||
failReason.value = data.fail_reason
|
||||
failReasonDialogVisible.value = true
|
||||
}
|
||||
|
||||
const knownToKnow = () => {
|
||||
Storage.set({ key: 'weappUploadTipsLock', data: true })
|
||||
uploadSuccessShowDialog.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -16,21 +16,28 @@
|
||||
<Warning />
|
||||
</el-icon>
|
||||
<div>
|
||||
<p class="text-base">{{ t('operationTip') }} 1、{{ t('operationTipOne') }}</p>
|
||||
<p class="text-base">2、{{ t('operationTipTwo') }}</p>
|
||||
<p class="text-base">{{ t('operationTipTwo') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<div>
|
||||
<el-table :data="cronTableData.data" size="large" v-loading="cronTableData.loading">
|
||||
<el-table :data="cronTableData.data" :span-method="templateSpan" size="large" v-loading="cronTableData.loading">
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !cronTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="name" :show-overflow-tooltip="true" :label="t('name')" min-width="150" />
|
||||
<el-table-column prop="addon_name" :label="t('addon')" min-width="120" />
|
||||
<el-table-column prop="name" :show-overflow-tooltip="true" :label="t('name')" min-width="150" >
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-[5px]">{{row.name }}</span>
|
||||
<el-tooltip :content="row.weapp.tips" v-if="row.weapp.tips" placement="top">
|
||||
<icon name="element-WarningFilled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('response')" min-width="180">
|
||||
<template #default="{ row }">
|
||||
@ -89,13 +96,44 @@ const loadCronList = (page: number = 1) => {
|
||||
|
||||
getTemplateList().then(res => {
|
||||
cronTableData.loading = false
|
||||
cronTableData.data = res.data
|
||||
let data = []
|
||||
res.data.forEach(item => {
|
||||
if (item.notice.length) {
|
||||
const addons = []
|
||||
Object.keys(item.notice).forEach((key, index) => {
|
||||
const notice = item.notice[key]
|
||||
notice.addon_name = item.title
|
||||
addons.push(notice)
|
||||
})
|
||||
if (addons.length) {
|
||||
addons[0].rowspan = addons.length
|
||||
data = data.concat(addons)
|
||||
}
|
||||
}
|
||||
})
|
||||
cronTableData.data = data
|
||||
}).catch(() => {
|
||||
cronTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadCronList()
|
||||
|
||||
const templateSpan = (row : any) => {
|
||||
if (row.columnIndex === 0) {
|
||||
if (row.row.rowspan) {
|
||||
return {
|
||||
rowspan: row.row.rowspan,
|
||||
colspan: 1
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rowspan: 0,
|
||||
colspan: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full p-5 bg-white">
|
||||
<div class="w-full p-5 bg-body">
|
||||
<div class="flex justify-between items-center mb-[20px]">
|
||||
<span class="text-page-title">{{ t('title') }}</span>
|
||||
</div>
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
<!-- 素材管理 -->
|
||||
<div v-if="attachment.data.length">
|
||||
<div class="flex flex-wrap" v-if="prop.type != 'news'">
|
||||
<div class="attachment-item mr-[10px] w-[120px]" v-for="(item, index) in attachment.data"
|
||||
<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]">
|
||||
|
||||
@ -12,32 +12,25 @@
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
<el-button type="primary" class="w-[100px]" @click="batchAcquisitionFn">{{ t('batchAcquisition')
|
||||
<el-button type="primary" class="w-[100px]" @click="batchAcquisitionFn()">{{ t('batchAcquisition')
|
||||
}}</el-button>
|
||||
</div>
|
||||
<el-alert class="warm-prompt !my-[20px]" type="info">
|
||||
<template #default>
|
||||
<div class="flex">
|
||||
<el-icon class="mr-2 mt-[2px]" size="18">
|
||||
<Warning />
|
||||
</el-icon>
|
||||
<div>
|
||||
<p class="text-base">{{ t('operationTip') }}</p>
|
||||
<p class="text-base">1、{{ t('operationTipOne') }}</p>
|
||||
<p class="text-base">2、{{ t('operationTipTwo') }}</p>
|
||||
<p class="text-base">3、{{ t('operationTipThree') }}</p>
|
||||
<p class="text-base">4、{{ t('operationTipFour') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="cronTableData.data" size="large" v-loading="cronTableData.loading">
|
||||
<el-table :data="cronTableData.data" :span-method="templateSpan" size="large" v-loading="cronTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !cronTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="name" :show-overflow-tooltip="true" :label="t('name')" min-width="150" />
|
||||
<el-table-column prop="addon_name" :label="t('addon')" min-width="120" />
|
||||
<el-table-column prop="name" :show-overflow-tooltip="true" :label="t('name')" min-width="150" >
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-[5px]">{{row.name }}</span>
|
||||
<el-tooltip :content="row.wechat.tips" v-if="row.wechat.tips" placement="top">
|
||||
<icon name="element-WarningFilled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('messageType')" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
@ -102,13 +95,44 @@ const loadCronList = (page: number = 1) => {
|
||||
|
||||
getTemplateList().then(res => {
|
||||
cronTableData.loading = false
|
||||
cronTableData.data = res.data
|
||||
}).catch(() => {
|
||||
let data = []
|
||||
res.data.forEach(item => {
|
||||
if (item.notice.length) {
|
||||
const addons = []
|
||||
Object.keys(item.notice).forEach((key, index) => {
|
||||
const notice = item.notice[key]
|
||||
notice.addon_name = item.title
|
||||
addons.push(notice)
|
||||
})
|
||||
if (addons.length) {
|
||||
addons[0].rowspan = addons.length
|
||||
data = data.concat(addons)
|
||||
}
|
||||
}
|
||||
})
|
||||
cronTableData.data = data
|
||||
}).catch((e) => {
|
||||
cronTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadCronList()
|
||||
|
||||
const templateSpan = (row : any) => {
|
||||
if (row.columnIndex === 0) {
|
||||
if (row.row.rowspan) {
|
||||
return {
|
||||
rowspan: row.row.rowspan,
|
||||
colspan: 1
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rowspan: 0,
|
||||
colspan: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取
|
||||
*/
|
||||
|
||||
@ -114,7 +114,7 @@
|
||||
<template #empty>
|
||||
<span>{{ !diyPageTable.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="title" :label="t('diyPageTitle')" min-width="120" />
|
||||
<el-table-column prop="page_title" :label="t('diyPageTitle')" min-width="120" />
|
||||
<el-table-column prop="addon_name" :label="t('diyPageTypeName')" min-width="80" />
|
||||
<el-table-column prop="type_name" :label="t('diyPageForAddon')" min-width="80" />
|
||||
</el-table>
|
||||
|
||||
101
admin/src/app/views/diy/components/edit-member-level.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<!-- 内容 -->
|
||||
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||
<div class="edit-attr-item-wrap">
|
||||
<h3 class="mb-[10px]">{{ t('selectStyle') }}</h3>
|
||||
<el-form label-width="80px" class="px-[10px]">
|
||||
<el-form-item :label="t('selectStyle')" class="flex">
|
||||
<span class="text-primary flex-1 cursor-pointer" @click="showStyle">{{ diyStore.editComponent.styleName }}</span>
|
||||
<el-icon>
|
||||
<ArrowRight />
|
||||
</el-icon>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-dialog v-model="showDialog" :title="t('selectStyle')" width="500px">
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
<template v-for="(item,index) in styleList" :key="index">
|
||||
<div :class="{ 'border-primary': selectStyle.value == item.value }" @click="changeStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] cursor-pointer border bg-gray-50">
|
||||
<img :src="img(item.url)" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="confirmStyle">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 样式 -->
|
||||
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
|
||||
|
||||
<!-- 组件样式 -->
|
||||
<slot name="style"></slot>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from '@/lang'
|
||||
import useDiyStore from '@/stores/modules/diy'
|
||||
import { img } from '@/utils/common'
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
const diyStore = useDiyStore()
|
||||
diyStore.editComponent.ignore = ['componentBgColor','componentBgUrl'] // 忽略公共属性
|
||||
|
||||
const selectStyle = reactive({
|
||||
title: diyStore.editComponent.styleName,
|
||||
value: diyStore.editComponent.style
|
||||
})
|
||||
|
||||
// 风格样式
|
||||
const showDialog = ref(false)
|
||||
|
||||
const showStyle = () => {
|
||||
showDialog.value = true
|
||||
selectStyle.title = diyStore.editComponent.styleName;
|
||||
selectStyle.value = diyStore.editComponent.style;
|
||||
}
|
||||
|
||||
const styleList = reactive([
|
||||
{
|
||||
url: 'static/resource/images/diy/member/member_level_style1.jpg',
|
||||
title: '风格1',
|
||||
value: 'style-1'
|
||||
},
|
||||
{
|
||||
url: 'static/resource/images/diy/member/member_level_style2.png',
|
||||
title: '风格2',
|
||||
value: 'style-2'
|
||||
},
|
||||
{
|
||||
url: 'static/resource/images/diy/member/member_level_style3.jpg',
|
||||
title: '风格3',
|
||||
value: 'style-3'
|
||||
}
|
||||
])
|
||||
|
||||
const changeStyle = (item:any) => {
|
||||
selectStyle.title = item.title;
|
||||
selectStyle.value = item.value;
|
||||
}
|
||||
|
||||
const confirmStyle = () => {
|
||||
diyStore.editComponent.styleName = selectStyle.title;
|
||||
diyStore.editComponent.style = selectStyle.value;
|
||||
showDialog.value = false
|
||||
}
|
||||
|
||||
defineExpose({})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -4,15 +4,87 @@
|
||||
<div class="edit-attr-item-wrap">
|
||||
<h3 class="mb-[10px]">{{ t('pageContent') }}</h3>
|
||||
<el-form label-width="80px" class="px-[10px]">
|
||||
<el-form-item :label="t('pageName')">
|
||||
<el-input v-model.trim="diyStore.global.title" :placeholder="t('pageNamePlaceholder')" clearable maxlength="12" show-word-limit/>
|
||||
<el-form-item :label="t('diyPageTitle')">
|
||||
<el-input v-model.trim="diyStore.pageTitle" :placeholder="t('diyPageTitlePlaceholder')" clearable maxlength="12" show-word-limit/>
|
||||
<div class="text-sm text-gray-400">{{ t('pageTitleTips') }}</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="edit-attr-item-wrap">
|
||||
<h3 class="mb-[10px]">{{ t('statusBarContent') }}</h3>
|
||||
<el-form label-width="80px" class="px-[10px]">
|
||||
<el-form-item :label="t('diyTitle')">
|
||||
<el-input v-model.trim="diyStore.global.title" :placeholder="t('diyTitlePlaceholder')" clearable maxlength="12" show-word-limit/>
|
||||
<div class="text-sm text-gray-400">{{ t('titleTips') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('selectStyle')" class="display-block">
|
||||
<div class="flex">
|
||||
<span class="text-primary flex-1 cursor-pointer" @click="showStyle">{{diyStore.global.topStatusBar.styleName}}</span>
|
||||
<el-icon>
|
||||
<ArrowRight />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400 leading-[1.5]">{{ t('styleShowTips') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('topStatusBarImg')" v-if="['style-2','style-3'].indexOf(diyStore.global.topStatusBar.style) > -1">
|
||||
<upload-image v-model="diyStore.global.topStatusBar.imgUrl" :limit="1" />
|
||||
<div class="text-sm text-gray-400 mt-[10px]">{{ t('topStatusBarImgTips') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('topStatusBarSearchName')" v-if="'style-3' == diyStore.global.topStatusBar.style">
|
||||
<el-input v-model.trim="diyStore.global.topStatusBar.inputPlaceholder" :placeholder="t('topStatusBarSearchNamePlaceholder')" clearable maxlength="12" show-word-limit/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('textAlign')" v-show="diyStore.global.topStatusBar.style == 'style-1'">
|
||||
<el-radio-group v-model="diyStore.global.topStatusBar.textAlign">
|
||||
<el-radio :label="'left'">{{ t('textAlignLeft') }}</el-radio>
|
||||
<el-radio :label="'center'">{{ t('textAlignCenter') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('link')" v-if="['style-2','style-3'].indexOf(diyStore.global.topStatusBar.style) > -1">
|
||||
<diy-link v-model="diyStore.global.topStatusBar.link" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="edit-attr-item-wrap">
|
||||
<h3 class="mb-[10px]">{{ t('bottomNavContent') }}</h3>
|
||||
<el-form label-width="80px" class="px-[10px]">
|
||||
<el-form-item :label="t('tabbar')" class="display-block">
|
||||
<el-switch v-model="diyStore.global.bottomTabBarSwitch"/>
|
||||
<div class="text-sm text-gray-400">{{ t('tabbarSwitchTips') }}</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="showDialog" :title="t('selectStyle')" width="800px">
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
<div class="flex items-center justify-center overflow-hidden w-[32%] h-[100px] mr-[2%] mb-[15px] cursor-pointer border bg-gray-50" :class="{ 'border-primary': selectStyle == 'style-1' }" @click="selectStyle = 'style-1'">
|
||||
<img class="max-w-[100%] max-h-[100%]" src="@/app/assets/images/diy/head/nav_style1.jpg" />
|
||||
</div>
|
||||
<div class="flex items-center justify-center overflow-hidden w-[32%] h-[100px] mr-[2%] mb-[15px] cursor-pointer border bg-gray-50" :class="{ 'border-primary': selectStyle == 'style-2' }" @click="selectStyle = 'style-2'">
|
||||
<img class="max-w-[100%] max-h-[100%]" src="@/app/assets/images/diy/head/nav_style2.jpg" />
|
||||
</div>
|
||||
<div class="flex items-center justify-center overflow-hidden w-[32%] h-[100px] mb-[15px] cursor-pointer border bg-gray-50" :class="{ 'border-primary': selectStyle == 'style-3' }" @click="selectStyle = 'style-3'">
|
||||
<img class="max-w-[100%] max-h-[100%]" src="@/app/assets/images/diy/head/nav_style3.jpg" />
|
||||
</div>
|
||||
<div class="flex items-center justify-center overflow-hidden w-[32%] h-[100px] mr-[2%] cursor-pointer border bg-gray-50" :class="{ 'border-primary': selectStyle == 'style-4' }" @click="selectStyle = 'style-4'">
|
||||
<img class="max-w-[100%] max-h-[100%]" src="@/app/assets/images/diy/head/nav_style4.jpg" />
|
||||
</div>
|
||||
<div class="flex items-center justify-center overflow-hidden w-[32%] h-[100px] mr-[2%] cursor-pointer border bg-gray-50" :class="{ 'border-primary': selectStyle == 'style-5' }" @click="selectStyle = 'style-5'">
|
||||
<img class="max-w-[100%] max-h-[100%]" src="@/app/assets/images/diy/head/nav_style5.png" />
|
||||
</div>
|
||||
<div class="flex items-center justify-center overflow-hidden w-[32%] h-[100px] cursor-pointer border bg-gray-50" :class="{ 'border-primary': selectStyle == 'style-6' }" @click="selectStyle = 'style-6'">
|
||||
<img class="max-w-[100%] max-h-[100%]" src="@/app/assets/images/diy/head/nav_style6.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="changeStyle">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<!-- 样式 -->
|
||||
@ -40,6 +112,18 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="edit-attr-item-wrap">
|
||||
<h3 class="mb-[10px]">{{ t('statusBarStyle') }}</h3>
|
||||
<el-form label-width="80px" class="px-[10px]">
|
||||
<el-form-item :label="t('topStatusBarBgColor')" class="display-block" v-if="selectStyle == 'style-5'">
|
||||
<el-color-picker v-model="diyStore.global.topStatusBar.bgColor" show-alpha />
|
||||
<div class="text-sm text-gray-400 leading-[1.5]">{{ t('topStatusBarBgColorTips') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('topStatusBarTextColor')" class="display-block">
|
||||
<el-color-picker v-model="diyStore.global.topStatusBar.textColor" show-alpha />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="edit-attr-item-wrap">
|
||||
<h3 class="mb-[10px]">{{ t('marginSet') }}</h3>
|
||||
<el-form label-width="80px" class="px-[10px]">
|
||||
@ -53,7 +137,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from '@/lang'
|
||||
import { watch } from 'vue'
|
||||
import { watch, ref, onMounted, computed } from 'vue'
|
||||
import useDiyStore from '@/stores/modules/diy'
|
||||
import { img } from '@/utils/common'
|
||||
|
||||
@ -74,13 +158,54 @@ watch(
|
||||
|
||||
// 改变页面的左右边距时,更新所有组件的数值
|
||||
const inputBoth = (value:any)=>{
|
||||
|
||||
diyStore.value.forEach((item,index)=>{
|
||||
item.margin.both = value;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
watch(
|
||||
() => diyStore.global,
|
||||
(newValue, oldValue) => {
|
||||
selectStyle.value = newValue.topStatusBar.style
|
||||
}, { deep: true }
|
||||
)
|
||||
|
||||
const showDialog = ref(false)
|
||||
const showStyle = () => {
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
const selectStyle = ref("style-1")
|
||||
const changeStyle = () => {
|
||||
diyStore.global.topStatusBar.isShow = true;
|
||||
diyStore.global.topStatusBar.isTransparent = false;
|
||||
switch (selectStyle.value) {
|
||||
case 'style-1':
|
||||
diyStore.global.topStatusBar.styleName = '风格1'
|
||||
break
|
||||
case 'style-2':
|
||||
diyStore.global.topStatusBar.styleName = '风格2'
|
||||
break
|
||||
case 'style-3':
|
||||
diyStore.global.topStatusBar.styleName = '风格3'
|
||||
break
|
||||
case 'style-4':
|
||||
diyStore.global.topStatusBar.styleName = '风格4'
|
||||
break
|
||||
case 'style-5':
|
||||
diyStore.global.topStatusBar.isTransparent = true;
|
||||
diyStore.global.topStatusBar.styleName = '风格5'
|
||||
break
|
||||
case 'style-6':
|
||||
diyStore.global.topStatusBar.isShow = false;
|
||||
diyStore.global.topStatusBar.styleName = '风格6'
|
||||
break
|
||||
}
|
||||
diyStore.global.topStatusBar.style = selectStyle.value
|
||||
showDialog.value = false
|
||||
}
|
||||
|
||||
defineExpose({})
|
||||
</script>
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
|
||||
<div class="edit-attr-item-wrap">
|
||||
<h3 class="mb-[10px]">{{ t('richTextContentSet') }}</h3>
|
||||
<editor v-model="diyStore.editComponent.html" height="600px" class="editor-width" />
|
||||
<editor v-model="diyStore.editComponent.html" :height="600" class="editor-width" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -51,8 +51,34 @@
|
||||
<el-scrollbar>
|
||||
<el-button class="page-btn absolute right-[20px]" @click="diyStore.changeCurrentIndex(-99)">{{ t('pageSet') }}</el-button>
|
||||
<div class="diy-view-wrap w-[375px] shadow-lg mx-auto">
|
||||
<div class="preview-head bg-no-repeat bg-center bg-cover" @click="diyStore.changeCurrentIndex(-99)">
|
||||
<span class="text-base block text-center truncate cursor-pointer h-[64px] leading-[84px]">{{ diyStore.global.title }}</span>
|
||||
<div class="preview-head bg-no-repeat bg-center bg-cover cursor-pointer h-[64px]" :class="[diyStore.global.topStatusBar.style]" :style="{backgroundColor :diyStore.global.topStatusBar.bgColor}" @click="diyStore.changeCurrentIndex(-99)">
|
||||
<div v-if="diyStore.global.topStatusBar.style == 'style-1'" class="content-wrap">
|
||||
<div class="title-wrap" :style="{ fontSize: '14px', color: diyStore.global.topStatusBar.textColor, textAlign: diyStore.global.topStatusBar.textAlign }">
|
||||
{{ diyStore.global.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="diyStore.global.topStatusBar.style == 'style-2'" class="content-wrap">
|
||||
<div class="title-wrap" :style="{ color: diyStore.global.topStatusBar.textColor }">
|
||||
<div class="h-[28px] max-w-[150px] mr-[8px]" v-if="diyStore.global.topStatusBar.imgUrl">
|
||||
<img class="max-w-[100%] max-h-[100%]" :src="img(diyStore.global.topStatusBar.imgUrl)" mode="heightFix" />
|
||||
</div>
|
||||
<div :style="{ color: diyStore.global.topStatusBar.textColor }">{{ diyStore.global.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="diyStore.global.topStatusBar.style == 'style-3'" class="content-wrap">
|
||||
<div class="title-wrap" v-if="diyStore.global.topStatusBar.imgUrl">
|
||||
<img class="max-w-[100%] max-h-[100%]" :src="img(diyStore.global.topStatusBar.imgUrl)" />
|
||||
</div>
|
||||
<div class="search">
|
||||
<span class="iconfont iconsousuo absolute left-[10px]"></span>
|
||||
<span class="text-[14px]">{{diyStore.global.topStatusBar.inputPlaceholder}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="diyStore.global.topStatusBar.style == 'style-4'" class="content-wrap">
|
||||
<span class="iconfont iconxiazai19 !text-[14px]" :style="{ color: diyStore.global.topStatusBar.textColor }"></span>
|
||||
<div class="title-wrap" :style="{ color: diyStore.global.topStatusBar.textColor }">我的位置</div>
|
||||
<span class="iconfont iconxiangyoujiantou !text-[12px]" :style="{ color: diyStore.global.topStatusBar.textColor }"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview-block relative">
|
||||
|
||||
@ -84,6 +110,7 @@
|
||||
<el-input v-model="wapDomain" :placeholder="t('wapDomainPlaceholder')" clearable />
|
||||
</div>
|
||||
<el-button type="primary" @click="saveWapDomain">{{ t('confirm') }}</el-button>
|
||||
<el-button type="primary" @click="settingTips()" plain>{{ t('settingTips') }}</el-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -121,7 +148,7 @@
|
||||
<icon name="iconfont-iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
|
||||
<el-color-picker v-model="diyStore.editComponent.pageEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
|
||||
</el-form-item>
|
||||
<div class="text-sm text-gray-400 ml-[80px] mb-[10px]">{{ t('bottomBgTips') }}</div>
|
||||
<div class="text-sm text-gray-400 ml-[90px] mb-[10px]">{{ t('bottomBgTips') }}</div>
|
||||
</template>
|
||||
|
||||
<el-form-item :label="t('bgGradientAngle')" v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
|
||||
@ -153,7 +180,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('marginTop')" v-if="diyStore.editComponent.ignore.indexOf('marginTop') == -1">
|
||||
<el-slider v-model="diyStore.editComponent.margin.top" show-input size="small" :min="0" class="ml-[10px] horz-blank-slider" />
|
||||
<el-slider v-model="diyStore.editComponent.margin.top" show-input size="small" :min="-100" class="ml-[10px] horz-blank-slider" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('marginBottom')" v-if="diyStore.editComponent.ignore.indexOf('marginBottom') == -1">
|
||||
<el-slider v-model="diyStore.editComponent.margin.bottom" show-input size="small" class="ml-[10px] horz-blank-slider" />
|
||||
@ -186,6 +213,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, toRaw, watch, inject } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { img } from '@/utils/common'
|
||||
import { getDiyTemplatePages, addDiyPage, editDiyPage, initPage } from '@/app/api/diy'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
@ -231,6 +259,7 @@ const handleChange = (val: string[]) => {
|
||||
const originData = reactive({
|
||||
id: diyStore.id,
|
||||
name: diyStore.name,
|
||||
pageTitle: diyStore.pageTitle,
|
||||
title: diyStore.global.title,
|
||||
value: JSON.stringify({
|
||||
global: toRaw(diyStore.global),
|
||||
@ -315,8 +344,8 @@ const changeTemplatePage = (value:any)=> {
|
||||
} else {
|
||||
// 清空
|
||||
diyStore.init();
|
||||
if(route.query.title) diyStore.global.title = diyStore.typeName
|
||||
}
|
||||
if(route.query.title) diyStore.global.title = route.query.title
|
||||
}).catch(() => {
|
||||
// 还原
|
||||
template.value = oldTemplate.value;
|
||||
@ -331,8 +360,8 @@ const changeTemplatePage = (value:any)=> {
|
||||
} else {
|
||||
// 清空
|
||||
diyStore.init();
|
||||
if(route.query.title) diyStore.global.title = diyStore.typeName
|
||||
}
|
||||
if(route.query.title) diyStore.global.title = route.query.title
|
||||
}
|
||||
};
|
||||
|
||||
@ -343,6 +372,7 @@ watch(
|
||||
const data = {
|
||||
id: newValue.id,
|
||||
name: newValue.name,
|
||||
pageTitle: newValue.pageTitle,
|
||||
title: newValue.global.title,
|
||||
value: JSON.stringify({
|
||||
global: toRaw(newValue.global),
|
||||
@ -368,6 +398,7 @@ initPage({
|
||||
diyStore.init() // 初始化清空数据
|
||||
diyStore.id = data.id || 0
|
||||
diyStore.name = data.name
|
||||
diyStore.pageTitle = data.page_title
|
||||
diyStore.type = data.type
|
||||
diyStore.typeName = data.type_name
|
||||
diyStore.templateName = data.template
|
||||
@ -387,6 +418,7 @@ initPage({
|
||||
// 初始化原数据
|
||||
originData.id = diyStore.id
|
||||
originData.name = diyStore.name
|
||||
originData.pageTitle = diyStore.pageTitle
|
||||
originData.title = diyStore.global.title
|
||||
originData.value = JSON.stringify({
|
||||
global: toRaw(diyStore.global),
|
||||
@ -583,6 +615,7 @@ const save = (callback: any) => {
|
||||
let data = {
|
||||
id: diyStore.id,
|
||||
name: diyStore.name,
|
||||
page_title: diyStore.pageTitle,
|
||||
title: diyStore.global.title,
|
||||
type: diyStore.type,
|
||||
template: diyStore.templateName,
|
||||
@ -623,6 +656,10 @@ const preview = () => {
|
||||
window.open(url.href)
|
||||
})
|
||||
}
|
||||
|
||||
const settingTips = () => {
|
||||
window.open('https://www.kancloud.cn/niucloud/niucloud-admin-develop/3213393')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -699,4 +736,101 @@ const preview = () => {
|
||||
.edit-attribute-wrap .box-card {
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
.diy-view-wrap .preview-head{
|
||||
padding: 28px 15px 0;
|
||||
.content-wrap{
|
||||
height: 30px;
|
||||
}
|
||||
&.style-1 {
|
||||
.content-wrap {
|
||||
.title-wrap {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.style-2 {
|
||||
.content-wrap {
|
||||
.title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> div {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
max-width: 150px;
|
||||
font-size: 14px;
|
||||
|
||||
&:last-child {
|
||||
overflow: hidden; //超出的文本隐藏
|
||||
text-overflow: ellipsis; //用省略号显示
|
||||
white-space: nowrap; //不换行
|
||||
flex: 1;
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.style-3 {
|
||||
.content-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.title-wrap {
|
||||
height: 30px;
|
||||
max-width: 85px;
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search {
|
||||
flex: 1;
|
||||
padding-right: 10px;
|
||||
padding-left: 31px;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
text-align: left;
|
||||
border-radius: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border: 1px solid #eeeeee;
|
||||
color: rgb(102, 102, 102);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 105px;
|
||||
overflow: hidden;
|
||||
span{
|
||||
overflow: hidden; //超出的文本隐藏
|
||||
text-overflow: ellipsis; //用省略号显示
|
||||
white-space: nowrap; //不换行
|
||||
}
|
||||
.iconfont {
|
||||
color: #909399;
|
||||
font-size: 16px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.style-4 {
|
||||
.content-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.title-wrap {
|
||||
flex: none;
|
||||
margin: 0 5px;
|
||||
max-width: 180px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<span>{{ !diyPageTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="title" :label="t('title')" min-width="120" />
|
||||
<el-table-column prop="page_title" :label="t('title')" min-width="120" />
|
||||
<el-table-column prop="addon_name" :label="t('forAddon')" min-width="80" />
|
||||
<el-table-column prop="type_name" :label="t('typeName')" min-width="80" />
|
||||
<el-table-column :label="t('status')" min-width="80">
|
||||
@ -129,7 +129,6 @@ import { t } from '@/lang'
|
||||
import { getApps,getDiyPageList, deleteDiyPage, getDiyTemplate, editDiyPageShare, setUseDiyPage } from '@/app/api/diy'
|
||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getUrl } from '@/app/api/sys'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -161,24 +160,19 @@ const addEvent = async (formEl: FormInstance | undefined) => {
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false
|
||||
const query = { type: formData.type, title: formData.title }
|
||||
const url = router.resolve({
|
||||
path: '/decorate/edit',
|
||||
query
|
||||
})
|
||||
window.open(url.href)
|
||||
dialogVisible.value = false
|
||||
formData.title = ''
|
||||
formData.type = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const wapDomain = ref('')
|
||||
const getDomain = async () => {
|
||||
wapDomain.value = (await getUrl()).data.wap_url
|
||||
}
|
||||
|
||||
getDomain()
|
||||
|
||||
// 获取自定义页面类型
|
||||
const loadDiyTemplate = (addon = '')=> {
|
||||
getDiyTemplate({mode: '', addon}).then(res => {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
|
||||
<div class="flex">
|
||||
<div class="w-[360px] h-[400px] absolute mr-[30px] border-[1px] border-gray-300">
|
||||
<div class="flex items-center justify-between absolute h-[60px] left-[0px] right-[0px] bottom-[0px] bg-white border-[1px] border-primary" :style="{ 'backgroundColor': diyBottomData.value.backgroundColor }">
|
||||
<div class="flex items-center justify-between absolute h-[60px] left-[0px] right-[0px] bottom-[0px] border-[1px] border-primary" :style="{ 'backgroundColor': diyBottomData.value.backgroundColor }">
|
||||
<div class="flex flex-1 flex-col items-center justify-center" v-for="(item, index) in diyBottomData.value.list" :key="'b' + index">
|
||||
<el-image class="w-[22px] h-[22px] mb-[5px] leading-1" :src="img(item.iconPath)" :fit="contain" v-if="['1', '2'].includes(diyBottomData.value.type.toString())">
|
||||
<template #error>
|
||||
@ -44,7 +44,7 @@
|
||||
<el-input class="w-[215px]" v-model="item.text" :placeholder="t('titleContent')" maxlength="5" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('navLinkOne')">
|
||||
<diy-link v-model="item.link"></diy-link>
|
||||
<diy-link v-model="item.link"/>
|
||||
</el-form-item>
|
||||
<el-icon class="close-icon cursor-pointer -top-[11px] -right-[8px]" @click="deleteNav(index)">
|
||||
<CircleCloseFilled />
|
||||
@ -259,4 +259,4 @@ onMounted(() => {
|
||||
.list-item:hover .close-icon {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main-container w-full pt-[64px] bg-white" v-loading="loading">
|
||||
<div class="main-container w-full pt-[64px] " v-loading="loading">
|
||||
<div class="flex justify-between items-center h-[32px] mb-4">
|
||||
<span class="text-page-title">{{ t('editPersonal') }}</span>
|
||||
</div>
|
||||
|
||||
@ -19,9 +19,11 @@
|
||||
<el-button type="primary" class="w-[90px] !h-[34px]" @click="handleChick">创建站点</el-button>
|
||||
</div>
|
||||
<div class="flex justify-between items-center mt-[18px]">
|
||||
<div class="flex items-center flex-wrap text-[14px] w-[800px]">
|
||||
<span :class="['mr-[12px] cursor-pointer', {'text-[var(--el-color-primary)]': params.app == ''}]" @click="cutAppFn('')">所有应用</span>
|
||||
<span :class="['mr-[12px] cursor-pointer', {'text-[var(--el-color-primary)]': params.app == item.key}]" @click="cutAppFn(item.key)" v-for="(item,index) in addonList" :key="index">{{item.title}}</span>
|
||||
<div class="w-[800px] text-[14px] whitespace-nowrap">
|
||||
<el-scrollbar :always="true">
|
||||
<span :class="['mr-[12px] cursor-pointer', {'text-[var(--el-color-primary)]': params.app == ''}]" @click="cutAppFn('')">所有应用</span>
|
||||
<span :class="['mr-[12px] cursor-pointer', {'text-[var(--el-color-primary)]': params.app == item.key}]" @click="cutAppFn(item.key)" v-for="(item,index) in addonList" :key="index">{{item.title}}</span>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<el-input v-model="params.keywords" class="!w-[300px] !h-[34px]" placeholder="请输入要搜索的站点名称" @keyup.enter.native="getHomeSiteFn()">
|
||||
<template #suffix>
|
||||
@ -37,7 +39,7 @@
|
||||
<div v-for="(item, index) in tableData" :key="index" @click="selectSite(item)"
|
||||
:class="['home-item w-[285px] box-border mb-[20px] cursor-pointer',{'mr-[20px]': index ==0 || (index+1)%4 != 0}]">
|
||||
<div class="flex items-center px-[24px] pt-[22px] pb-[16px] bg-[#F0F2F4] home-item-head">
|
||||
<img v-if="item.logo" class="w-[48px] h-[48px] mr-[15px] rounded-[50%] overflow-hidden" :src="img(item.logo)" />
|
||||
<img v-if="item.icon" class="w-[48px] h-[48px] mr-[15px] rounded-[50%] overflow-hidden" :src="img(item.icon)" />
|
||||
<img v-else class="w-[48px] h-[48px] mr-[15px] rounded-[50%] overflow-hidden" src="@/app/assets/images/member_head.png" />
|
||||
<div class="flex flex-col flex-1 justify-center">
|
||||
<div class="flex items-center flex-wrap">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="main-container w-full pt-[64px] bg-white" v-loading="loading">
|
||||
<div class="main-container w-full pt-[64px] " v-loading="loading">
|
||||
<div class="flex justify-between items-center h-[32px] mb-4">
|
||||
<span class="text-page-title">{{ t("personal") }}</span>
|
||||
<span class="text-[14px] text-[#999] cursor-pointer" @click="toEditPersonal">{{ t("editPersonal") }}</span>
|
||||
|
||||
@ -2,29 +2,8 @@
|
||||
<template>
|
||||
<div v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[24px] font-600 text-[#242424] leading-[33px]">欢迎使用niucloud-admin</span>
|
||||
<div class="ml-[12px] bg-[#333] flex items-center py-[3px] px-[6px] rounded">
|
||||
<img class="w-[16px] h-[16px]" src="@/app/assets/images/SaaS.png" />
|
||||
<span class="text-[12px] text-[#fff] font-600 leading-[16px] ml-[3px]">SAAS版</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center mt-[12px]">
|
||||
<img class="w-[12px] h-[12px]" src="@/app/assets/images/versions.png" />
|
||||
<span class="ml-[7px] text-[16px] font-600 text-[#424242] leading-[16px] font-[600]">{{ statInfo.version.version }}</span>
|
||||
<div class="ml-[10px] cursor-pointer" v-if="newVersion" @click="toUpgrade">
|
||||
<el-tag type="danger" size="small">{{ t('newVersion') }}{{ newVersion.last_version }}</el-tag>
|
||||
</div>
|
||||
<el-link class="text-color ml-[30px] text-[14px] leading-[20px]" href="https://www.niucloud.com/"
|
||||
target="_blank" :underline="false">{{ t("officialWbsite") }}</el-link>
|
||||
<el-link class="ml-[12px] text-color text-[14px] leading-[20px]"
|
||||
href="https://gitee.com/niucloud-team/niucloud.git" target="_blank"
|
||||
:underline="false">Gitee</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="px-[32px] pt-[24px] pb-[14px] bg-[#fff] border-[1px] border-[#E9EBF0] border-solid mt-[42px] box-border">
|
||||
class="px-[32px] pt-[24px] pb-[14px] bg-[#fff] border-[1px] border-[#E9EBF0] border-solid box-border">
|
||||
<el-card class="box-card !border-none profile-data" shadow="never"
|
||||
:body-style="{ padding: '49px 32px 20px' }">
|
||||
<template #header>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center h-[32px] mb-4">
|
||||
<span class="text-page-title text-[#222]">{{ t('localAppText') }}</span>
|
||||
<el-input class="!w-[250px]" :placeholder="t('search')" v-model="search_name" @keyup.enter="query">
|
||||
<el-input class="!w-[250px]" :placeholder="t('search')" v-model.trim="search_name" @keyup.enter="query">
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon cursor-pointer" size="14px" @click="query">
|
||||
<search />
|
||||
@ -182,13 +182,13 @@
|
||||
<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="formData.auth_code" :placeholder="t('authCodePlaceholder')"
|
||||
<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="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')"
|
||||
<el-input v-model.trim="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')"
|
||||
class="input-width" size="large" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
@ -377,7 +377,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 下载提示 -->
|
||||
<el-dialog v-model="unloadHintDialog" title="下载提示" width="30%" :before-close="handleClose">
|
||||
<el-dialog v-model="unloadHintDialog" title="下载提示" width="30%">
|
||||
<span>本地已经存在该插件/应用,再次下载会覆盖该插件/应用。</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
@ -636,6 +636,8 @@ const getInstallTask = (first: boolean = true) => {
|
||||
notificationEl.close()
|
||||
}
|
||||
}
|
||||
}).catch((e) => {
|
||||
terminalRef.value.pushMessage({ content: e.message, class: 'error' })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
<img src="@/app/assets/images/tools/official_market.png" class="w-[256px] h-[128px]" />
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog v-model="developerDialogVisible" class="developer-dialog-wrap" title="开发人员模式说明" width="30%" :before-close="handleClose">
|
||||
<el-dialog v-model="developerDialogVisible" class="developer-dialog-wrap" title="开发人员模式说明" width="30%">
|
||||
<div>
|
||||
<p class="text-[16px] mb-[4px] text-[#333]">开发模式</p>
|
||||
<div class="text-[14px] indent-[2em]">
|
||||
|
||||
@ -159,14 +159,6 @@ const webSite = computed(() => useSystemStore().website)
|
||||
// 判断登录页面[平台或者站点]
|
||||
const loginType = ref(getAppType())
|
||||
|
||||
watch(() => webSite.value, () => {
|
||||
if (loginType.value == 'site') {
|
||||
setWindowTitle(webSite.value.site_name+'-'+t('siteLogin'))
|
||||
} else {
|
||||
setWindowTitle(webSite.value.site_name+'-'+t('adminLogin'))
|
||||
}
|
||||
})
|
||||
|
||||
// 验证码 - start
|
||||
const verifyRef = ref(null)
|
||||
const success = (params:any) => {
|
||||
|
||||
148
admin/src/app/views/marketing/components/sign-continue.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<el-form :model="formData" :rules="formRules" class="page-form" ref="formRef">
|
||||
<el-form-item :label="t('continueSign')" prop="continue_sign">
|
||||
<el-input class="input-width" v-model.trim="formData.continue_sign" clearable /><span class="ml-[10px]">{{ t('day')
|
||||
}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('continueSign')" >
|
||||
<div>
|
||||
<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" />
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('receiveLimit')" prop="receive_num">
|
||||
<div>
|
||||
<el-radio class="mb-[15px]" v-model="formData.receive_limit" :label="1" @change="radioChange($event, 1)">{{
|
||||
t('noLimit') }}</el-radio>
|
||||
<div class="flex">
|
||||
<el-radio class="!mr-[15px]" v-model="formData.receive_limit" :label="2" @change="radioChange($event, 2)">{{
|
||||
t('everyOneLimit') }}</el-radio>
|
||||
<el-input class="input-width" v-model="formData.receive_num" clearable /><span class="ml-[10px]">{{
|
||||
t('time') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { t } from '@/lang'
|
||||
import { ref, reactive, defineAsyncComponent, computed, watch } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import { getGiftDict } from '@/app/api/member'
|
||||
import { guid } from '@/utils/common'
|
||||
import Test from '@/utils/test'
|
||||
const gifts = ref({})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const formData = ref({
|
||||
continue_sign: 0,
|
||||
continue_tag: guid(),
|
||||
receive_limit: 1,
|
||||
receive_num: 0,
|
||||
})
|
||||
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
const giftRefs = ref([])
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const modules: any = import.meta.glob('@/**/*.vue')
|
||||
getGiftDict().then(({ data }) => {
|
||||
Object.keys(data).forEach((key: string) => {
|
||||
data[key].component && (data[key].component = defineAsyncComponent(modules[data[key].component]))
|
||||
})
|
||||
gifts.value = data
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
continue_sign: [
|
||||
{ required: true, message: t('continueSignPlaceholder'), trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
console.log(formData.value.continue_sign)
|
||||
if (!Test.digits(formData.value.continue_sign)) {
|
||||
callback('连续签到格式错误')
|
||||
} else if (formData.value.continue_sign <= 0) {
|
||||
callback('连续签到不能小于等于0')
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
receive_num: [
|
||||
{ required: true, message: t('receiveNumPlaceholder'), trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
if (formData.value.receive_limit == 2) {
|
||||
if (Test.empty(formData.value.receive_num)) {
|
||||
callback('请输入限领次数')
|
||||
}
|
||||
if (!Test.digits(formData.value.receive_num)) {
|
||||
callback('限领次数格式错误')
|
||||
}
|
||||
if (formData.value.receive_num <= 0) {
|
||||
callback('限领次数不能小于等于0')
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
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
|
||||
}
|
||||
|
||||
const radioChange = (val, key) => {
|
||||
formData[key] = val
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.input-width {
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
71
admin/src/app/views/marketing/components/sign-day.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div v-for="(item,index) in gifts" :key="index" class="day-sign">
|
||||
<component :is="item.component" v-model="formData[item.key]" ref="giftRefs" v-if="item.component" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineAsyncComponent, computed, watch } from 'vue'
|
||||
import { getGiftDict } from '@/app/api/member'
|
||||
|
||||
const gifts = ref({})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const formData = ref({})
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
const giftRefs = ref([])
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const modules: any = import.meta.glob('@/**/*.vue')
|
||||
getGiftDict().then(({ data }) => {
|
||||
Object.keys(data).forEach((key: string) => {
|
||||
data[key].component && (data[key].component = defineAsyncComponent(modules[data[key].component]))
|
||||
})
|
||||
gifts.value = data
|
||||
})
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
for (let i = 0; i < giftRefs.value.length; i++) {
|
||||
const item = giftRefs.value[i]
|
||||
!await item.verify() && (verify = false)
|
||||
}
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.day-sign:nth-child(1) :deep(.el-form-item__error) {
|
||||
left:48px;
|
||||
}
|
||||
.day-sign:nth-child(2) :deep(.el-form-item__error) {
|
||||
left:48px;
|
||||
}
|
||||
</style>
|
||||
417
admin/src/app/views/marketing/sign_config.vue
Normal file
@ -0,0 +1,417 @@
|
||||
<template>
|
||||
<div class="main-container bg-[#fff] rounded-[4px]">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center mb-[5px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none my-[10px]" shadow="never">
|
||||
<el-form :model="formData" label-width="150px" ref="ruleFormRef" :rules="formRules" class="page-form"
|
||||
v-loading="loading">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('signRule') }}</h3>
|
||||
<el-form-item :label="t('isUse')">
|
||||
<el-switch v-model="formData.is_use" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('signPeriod')" v-if="formData.is_use">
|
||||
<el-input-number v-model="formData.sign_period" clearable class="input-width"
|
||||
controls-position="right" /><span class="ml-[10px]">天</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('daySignAward')" prop="formData.day_award" v-if="formData.is_use">
|
||||
<div v-for="(item, index) in daySignAwardText" :key="index">
|
||||
<span v-if="item.is_use == '1'">{{ item.content }} </span>
|
||||
</div>
|
||||
<span class="cursor-pointer tutorial-btn ml-[5px]" @click="daySignAwardSet"
|
||||
v-if="formData.day_award == ''">{{ t('set') }}</span>
|
||||
<div class="flex ml-[5px]" v-else>
|
||||
<span class="cursor-pointer tutorial-btn" @click="daySignAwardSet">{{ t('modify') }}</span>
|
||||
<span class="ml-[5px] mr-[5px]">|</span>
|
||||
<span class="cursor-pointer tutorial-btn" @click="daySignAwardDel">{{ t('delete') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-tip">{{ t('daySignAwardTip') }}</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('continueSignAward')" prop="formData.continue_award" v-if="formData.is_use">
|
||||
<div>
|
||||
<div class="form-tip">{{ t('continueSignAwardTipTop') }}</div>
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="continueSignAwardTableData.data" size="large"
|
||||
v-loading="continueSignAwardTableData.loading">
|
||||
<template #empty>
|
||||
<span>{{ !continueSignAwardTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="continue_sign" :label="t('continueSign')" min-width="120" />
|
||||
|
||||
<el-table-column :label="t('continueSignAward')" min-width="300">
|
||||
<template #default="{ row }">
|
||||
<div v-for="(item, index) in row.continue_award" :key="index">
|
||||
<span v-if="item.is_use == '1'">{{ item.content }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('receiveLimit')" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.receive_limit == 1">{{ t('noLimit') }}</span>
|
||||
<span v-else>{{ t('everyOneLimit') }}{{ row.receive_num }}{{
|
||||
t('time') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="130">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link
|
||||
@click="continueSignAwardModify(true, scope.$index)">{{
|
||||
t('modify') }}</el-button>
|
||||
<el-button type="primary" link
|
||||
@click="deleteContinueSignAwardEvent(scope.$index)">{{
|
||||
t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="flex mt-[10px]">
|
||||
<span class="cursor-pointer tutorial-btn" @click="continueSignAwardSet">{{
|
||||
t('add') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="form-tip">{{ t('continueSignAwardTipBottom') }}</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="t('ruleExplain')" prop="formData.rule_explain" v-if="formData.is_use">
|
||||
<div class="flex">
|
||||
<el-input v-model="formData.rule_explain" :placeholder="t('ruleExplainTip')" type="textarea"
|
||||
rows="5" class="textarea-width" clearable />
|
||||
<el-button class="ml-[20px]" type="primary" @click="defaultExplainEvent()" plain>{{
|
||||
t('useDefaultExplain') }}</el-button>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-form>
|
||||
<!-- 日签奖励 -->
|
||||
<el-dialog v-model="daySignDialog" :title="t('daySignTitle')" width="1200px" :destroy-on-close="true"
|
||||
v-if="formData.is_use">
|
||||
<sign-day ref="benefitsRef" v-model="formData.day_award" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="daySignDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="setDaySignAward()">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 连签奖励 -->
|
||||
<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" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="continueSignDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="setContinueSignAward()">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" @click="onSave(ruleFormRef)">{{ t('save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getSignConfig, setSignConfig, getMemberGiftsContent } from '@/app/api/member'
|
||||
import signDay from '@/app/views/marketing/components/sign-day.vue'
|
||||
import signContinue from '@/app/views/marketing/components/sign-continue.vue'
|
||||
import { FormInstance, FormRules } from 'element-plus'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const tab = ref('signSet')
|
||||
|
||||
const loading = ref(true)
|
||||
const daySignDialog = ref(false)
|
||||
const continueSignDialog = ref(false)
|
||||
const ruleFormRef = ref<FormInstance>()
|
||||
const continue_award = ref({})
|
||||
let isEdit = false // 是否为编辑状态
|
||||
let editIndex = 0 // 连签奖励修改下标
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
sign_period: [
|
||||
{ required: true, message: t('signPeriodTip'), trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
/**
|
||||
* 签到奖励文本请求参数
|
||||
*/
|
||||
const contentData = reactive<Record<string, any>>({
|
||||
gifts: [],
|
||||
})
|
||||
|
||||
/**
|
||||
* 连签奖励显示文本
|
||||
*/
|
||||
const continueSignAwardText = ref([])
|
||||
|
||||
const formData = reactive<Record<string, any>>({
|
||||
is_use: 0,
|
||||
sign_period: 30,
|
||||
day_award: '',
|
||||
continue_award: [],
|
||||
rule_explain: '',
|
||||
})
|
||||
|
||||
const newArr = reactive<Record<string, any>>({
|
||||
receive_num: '',
|
||||
continue_sign: '',
|
||||
receive_limit: '',
|
||||
continue_award: []
|
||||
})
|
||||
|
||||
/**
|
||||
* 连签奖励table数据
|
||||
*/
|
||||
const continueSignAwardTableData = reactive<Record<string, any>>({
|
||||
loading: false,
|
||||
data: []
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取显示签到设置
|
||||
*/
|
||||
const setFormData = async () => {
|
||||
const data = await (await getSignConfig()).data
|
||||
Object.keys(formData).forEach((key: string) => {
|
||||
if (data[key] != undefined) formData[key] = data[key]
|
||||
})
|
||||
|
||||
if (formData.day_award) {
|
||||
contentData.gifts = formData.day_award
|
||||
setMemberBenefitsContent()
|
||||
}
|
||||
|
||||
if (formData.continue_award) {
|
||||
formData.continue_award.forEach((item: any, index: number) => {
|
||||
|
||||
continueSignAwardTableData.data.push(JSON.parse(JSON.stringify(item)))
|
||||
|
||||
contentData.gifts = [];
|
||||
|
||||
const val = JSON.parse(JSON.stringify(item))
|
||||
|
||||
delete val['continue_sign'];
|
||||
delete val['continue_tag'];
|
||||
delete val['receive_limit'];
|
||||
delete val['receive_num'];
|
||||
|
||||
contentData.gifts = val
|
||||
|
||||
setMemberBenefitsContents(contentData, item, index)
|
||||
})
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
}
|
||||
setFormData()
|
||||
|
||||
/**
|
||||
* 获取日签奖励显示文本
|
||||
*/
|
||||
const daySignAwardText = ref([])
|
||||
const setMemberBenefitsContent = async () => {
|
||||
const data = await (await getMemberGiftsContent(contentData)).data
|
||||
daySignAwardText.value = [];
|
||||
Object.values(data).forEach((el: any) => {
|
||||
daySignAwardText.value.push(el)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取连签奖励显示文本
|
||||
* @param content 请求参数
|
||||
* @param item 连签奖励数据
|
||||
* @param index 数组索引
|
||||
* @param tag 0默认数据加载
|
||||
*/
|
||||
const setMemberBenefitsContents = async (content: any, item: any, index: number = 0, tag = 0) => {
|
||||
const data = await (await getMemberGiftsContent(content)).data
|
||||
continueSignAwardText.value = [];
|
||||
Object.values(data).forEach((el: any) => {
|
||||
continueSignAwardText.value.push(el)
|
||||
})
|
||||
|
||||
newArr.receive_num = item.receive_num
|
||||
newArr.continue_sign = item.continue_sign
|
||||
newArr.receive_limit = item.receive_limit
|
||||
newArr.continue_award = continueSignAwardText.value
|
||||
|
||||
|
||||
if (!isEdit) {
|
||||
if (tag == 0) {
|
||||
continueSignAwardTableData.data.splice(index, 1, JSON.parse(JSON.stringify(newArr)))
|
||||
} else {
|
||||
continueSignAwardTableData.data.push(JSON.parse(JSON.stringify(newArr)))
|
||||
}
|
||||
} else {
|
||||
continueSignAwardTableData.data.splice(editIndex, 1, JSON.parse(JSON.stringify(newArr)))
|
||||
}
|
||||
|
||||
isEdit = false
|
||||
editIndex = 0
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置签到设置
|
||||
*/
|
||||
const onSave = async (formEl: FormInstance | undefined) => {
|
||||
if (loading.value || !formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const save = JSON.parse(JSON.stringify(formData))
|
||||
setSignConfig(save).then(() => {
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开日签奖励设置页
|
||||
*/
|
||||
const daySignAwardSet = () => {
|
||||
daySignDialog.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 日签奖励设置
|
||||
*/
|
||||
const benefitsRef = ref(null)
|
||||
const setDaySignAward = async () => {
|
||||
if (!await benefitsRef.value?.verify()) return
|
||||
console.log(formData.day_award)
|
||||
daySignDialog.value = false
|
||||
if (!formData.day_award.hasOwnProperty('balance') && !formData.day_award.hasOwnProperty('point') && formData.day_award.shop_coupon.is_use == 0) {
|
||||
formData.day_award = ''
|
||||
}
|
||||
contentData.gifts = formData.day_award
|
||||
|
||||
setMemberBenefitsContent()
|
||||
}
|
||||
|
||||
/**
|
||||
* 日签奖励删除
|
||||
*/
|
||||
const daySignAwardDel = () => {
|
||||
formData.day_award = ''
|
||||
daySignAwardText.value = []
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开连签奖励设置页
|
||||
*/
|
||||
const continueSignAwardSet = () => {
|
||||
continue_award.value = ''
|
||||
continueSignDialog.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改连签奖励设置页
|
||||
*/
|
||||
const continueSignAwardModify = (flag: boolean, index: any) => {
|
||||
isEdit = flag;
|
||||
editIndex = index;
|
||||
|
||||
continue_award.value = formData.continue_award[index]
|
||||
continueSignDialog.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 连签奖励设置
|
||||
*/
|
||||
const continueRef = ref(null)
|
||||
const setContinueSignAward = async () => {
|
||||
if (!await continueRef.value?.verify()) return
|
||||
continueSignDialog.value = false
|
||||
|
||||
if (!continue_award.value.hasOwnProperty('balance') && !continue_award.value.hasOwnProperty('point') && continue_award.value.shop_coupon.is_use == 0) {
|
||||
continue_award.value = ''
|
||||
}
|
||||
|
||||
if (Object.keys(continue_award.value).length > 0) {
|
||||
const val = JSON.parse(JSON.stringify(continue_award.value))
|
||||
|
||||
delete val['continue_sign'];
|
||||
delete val['continue_tag'];
|
||||
delete val['receive_limit'];
|
||||
delete val['receive_num'];
|
||||
|
||||
contentData.gifts = val
|
||||
|
||||
let index = 0;
|
||||
if (formData.continue_award.length > 0) {
|
||||
index = formData.continue_award.length - 1
|
||||
}
|
||||
|
||||
setMemberBenefitsContents(contentData, continue_award.value, 0, 1)
|
||||
|
||||
|
||||
if (!isEdit) {
|
||||
formData.continue_award.push(JSON.parse(JSON.stringify(continue_award.value)))
|
||||
} else {
|
||||
formData.continue_award.splice(editIndex, 1, JSON.parse(JSON.stringify(continue_award.value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连签奖励删除
|
||||
*/
|
||||
const deleteContinueSignAwardEvent = (index: number) => {
|
||||
continueSignAwardTableData.data.splice(index, 1)
|
||||
formData.continue_award.splice(index, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认说明
|
||||
*/
|
||||
const defaultExplainEvent = () => {
|
||||
formData.rule_explain = t('ruleExplainDefault');
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.input-width {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.textarea-width {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.el-form .form-tip {
|
||||
line-height: 1.5;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.tutorial-btn {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
</style>
|
||||
156
admin/src/app/views/marketing/sign_list.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center mb-[5px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none mb-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="memberSignListTableData.searchParam" ref="searchFormRef">
|
||||
|
||||
<el-form-item :label="t('memberInfo')" prop="keywords">
|
||||
<el-input v-model="memberSignListTableData.searchParam.keywords" class="w-[240px]"
|
||||
:placeholder="t('memberInfoPlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('createTime')" prop="create_time">
|
||||
<el-date-picker v-model="memberSignListTableData.searchParam.create_time" type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
|
||||
:end-placeholder="t('endDate')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadMemberSignList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</el-card>
|
||||
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="memberSignListTableData.data" size="large" v-loading="memberSignListTableData.loading">
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !memberSignListTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<el-table-column prop="member_id" :label="t('memberId')" min-width="100" :show-overflow-tooltip="true">
|
||||
<template #default="{ row }">
|
||||
{{ row.member.member_no }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('memberInfo')" min-width="140" :show-overflow-tooltip="true">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center cursor-pointer" @click="toMember(row.member_id)">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg"
|
||||
:src="img(row.member.headimg)" alt="">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-else
|
||||
src="@/app/assets/images/default_headimg.png" alt="">
|
||||
<div class="flex flex flex-col">
|
||||
<span>{{ row.member.nickname || '' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" :label="t('mobile')" min-width="90">
|
||||
<template #default="{ row }">
|
||||
{{ row.member.mobile || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('days')" min-width="110">
|
||||
<template #default="{ row }">
|
||||
{{ row.days }}{{ t('day') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('dayAward')" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.day_award">
|
||||
<div v-if="row.day_award.balance.is_use">{{ row.day_award.balance.content }}</div>
|
||||
<div v-if="row.day_award.point.is_use">{{ row.day_award.point.content }}</div>
|
||||
<div v-if="row.day_award.shop_coupon.is_use">{{ row.day_award.shop_coupon.content }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('continueAward')" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.continue_award">
|
||||
<div v-if="row.continue_award.balance.is_use">{{ row.continue_award.balance.content }}</div>
|
||||
<div v-if="row.continue_award.point.is_use">{{ row.continue_award.point.content }}</div>
|
||||
<div v-if="row.continue_award.shop_coupon.is_use">{{
|
||||
row.continue_award.shop_coupon.content }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" :show-overflow-tooltip="true" :label="t('createTime')"
|
||||
min-width="150" />
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="memberSignListTableData.page"
|
||||
v-model:page-size="memberSignListTableData.limit" layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="memberSignListTableData.total" @size-change="loadMemberSignList()"
|
||||
@current-change="loadMemberSignList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getMemberSignList } from '@/app/api/member'
|
||||
import { FormInstance } from 'element-plus'
|
||||
import { img } from '@/utils/common'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const pageName = route.meta.title
|
||||
const memberSignListTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
keywords: '',
|
||||
create_time: '',
|
||||
member_id: ''
|
||||
}
|
||||
})
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
loadMemberSignList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员账单表列表
|
||||
*/
|
||||
const loadMemberSignList = (page: number = 1) => {
|
||||
memberSignListTableData.loading = true
|
||||
memberSignListTableData.page = page
|
||||
|
||||
getMemberSignList({
|
||||
page: memberSignListTableData.page,
|
||||
limit: memberSignListTableData.limit,
|
||||
...memberSignListTableData.searchParam
|
||||
}).then(res => {
|
||||
memberSignListTableData.loading = false
|
||||
memberSignListTableData.data = res.data.data
|
||||
memberSignListTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
memberSignListTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadMemberSignList()
|
||||
|
||||
/**
|
||||
* 会员详情
|
||||
*/
|
||||
const toMember = (member_id: number) => {
|
||||
router.push(`/member/detail?id=${member_id}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
227
admin/src/app/views/marketing/verifier.vue
Normal file
@ -0,0 +1,227 @@
|
||||
<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="addEvent">{{ t('addVerifier') }}</el-button>
|
||||
</div>
|
||||
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="verifierTable.data" size="large" v-loading="verifierTable.loading">
|
||||
<template #empty>
|
||||
<span>{{ !verifierTable.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
<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]" v-else
|
||||
src="@/app/assets/images/default_headimg.png" alt="">
|
||||
<div class="flex flex flex-col">
|
||||
<span>{{ row.member.nickname || '' }}</span>
|
||||
<span>{{ row.member.mobile || '' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('createTime')" prop="create_time" min-width="120" />
|
||||
<el-table-column :label="t('operation')" fixed="right" align="right" width="100">
|
||||
<template #default="{ row }">
|
||||
<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="verifierTable.page" v-model:page-size="verifierTable.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="verifierTable.total"
|
||||
@size-change="loadVerifierList()" @current-change="loadVerifierList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<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>
|
||||
</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>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="addLoading" @click="addVerifiers(formRef)">{{ t('confirm')
|
||||
}}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { getVerifierList, deleteVerifier, addVerifier, getVerifyTypeList } from '@/app/api/verify'
|
||||
import { getMemberList } from '@/app/api/member'
|
||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { img } from '@/utils/common'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
|
||||
const showDialog = ref(false)
|
||||
const addLoading = ref(false)
|
||||
const formData: Record<string, any> = reactive({
|
||||
member_id: '',
|
||||
verify_type: '',
|
||||
})
|
||||
|
||||
const formRules = reactive({
|
||||
member_id: [
|
||||
{ required: true, message: t('memberIdPlaceholder'), trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const verifierTable = reactive<any>({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: []
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取核销员表列表
|
||||
*/
|
||||
const loadVerifierList = (page: number = 1) => {
|
||||
verifierTable.loading = true
|
||||
verifierTable.page = page
|
||||
|
||||
getVerifierList({
|
||||
page: verifierTable.page,
|
||||
limit: verifierTable.limit,
|
||||
...verifierTable.searchParam
|
||||
}).then(res => {
|
||||
verifierTable.loading = false
|
||||
verifierTable.data = res.data.data
|
||||
verifierTable.total = res.data.total
|
||||
}).catch(() => {
|
||||
verifierTable.loading = false
|
||||
})
|
||||
}
|
||||
loadVerifierList()
|
||||
|
||||
/**
|
||||
* 添加核销员表
|
||||
*/
|
||||
const addEvent = () => {
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除核销员表
|
||||
*/
|
||||
const deleteEvent = (id: number) => {
|
||||
ElMessageBox.confirm(t('verifierDeleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
deleteVerifier(id).then(() => {
|
||||
loadVerifierList()
|
||||
}).catch(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加核销员
|
||||
* @param formEl
|
||||
*/
|
||||
const addVerifiers = async (formEl: FormInstance | undefined) => {
|
||||
if (addLoading.value || !formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
addLoading.value = true
|
||||
|
||||
addVerifier(formData).then(res => {
|
||||
addLoading.value = false
|
||||
showDialog.value = false
|
||||
formData.member_id = ''
|
||||
formData.verify_type = ''
|
||||
loadVerifierList()
|
||||
}).catch(() => {
|
||||
addLoading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询会员信息
|
||||
*/
|
||||
const memberList = ref<any>([])
|
||||
const searchLoading = ref(false)
|
||||
const searchMember = (query: string) => {
|
||||
if (query) {
|
||||
searchLoading.value = true
|
||||
getMemberList({ keyword: query }).then(res => {
|
||||
memberList.value = res.data.data
|
||||
searchLoading.value = false
|
||||
}).catch()
|
||||
} else {
|
||||
memberList.value = []
|
||||
searchLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取核销类型
|
||||
*/
|
||||
const verifyTypeList = ref<any>([])
|
||||
const setVerifyTypeList = () => {
|
||||
getVerifyTypeList().then(res => {
|
||||
verifyTypeList.value = res.data
|
||||
}).catch()
|
||||
}
|
||||
setVerifyTypeList();
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
/**
|
||||
* 会员详情
|
||||
*/
|
||||
const toMember = (member_id: number) => {
|
||||
router.push(`/member/detail?id=${member_id}`)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
143
admin/src/app/views/marketing/verify.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
|
||||
<el-card class="box-card !border-none table-search-wrap" shadow="never">
|
||||
<el-card class="box-card !border-none table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="recordTable.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('verifyCode')" prop="code">
|
||||
<el-input v-model="recordTable.searchParam.code" :placeholder="t('verifyCodePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('verifyType')" prop="type">
|
||||
<el-select v-model="recordTable.searchParam.type" clearable
|
||||
:placeholder="t('verifyTypePlaceholder')" class="input-width">
|
||||
<el-option :label="t('selectPlaceholder')" value="" />
|
||||
<el-option :label="item.name" :value="key" v-for="(item, key) in verifyTypeList"
|
||||
:key="key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('verifyTime')" prop="create_time">
|
||||
<el-date-picker v-model="recordTable.searchParam.create_time" type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
|
||||
:end-placeholder="t('endDate')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadRecordList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="recordTable.data" size="large" v-loading="recordTable.loading">
|
||||
<template #empty>
|
||||
<span>{{ !recordTable.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="code" :show-overflow-tooltip="true" :label="t('verifyCode')" align="left"
|
||||
min-width="150" />
|
||||
<el-table-column prop="type_name" :label="t('verifyType')" align="left" min-width="150" />
|
||||
<el-table-column :label="t('verifyTime')" min-width="180" align="center"
|
||||
:show-overflow-tooltip="true">
|
||||
<template #default="{ row }">
|
||||
{{ row.create_time || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="member.nickname" :label="t('verifyer')" min-width="180" align="center">
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="100">
|
||||
<template #default="{ row }">
|
||||
<div class="flex justify-end">
|
||||
<el-button type="primary" link @click="detailEvent(row)">{{ t('详情') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="recordTable.page" v-model:page-size="recordTable.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="recordTable.total"
|
||||
@size-change="loadRecordList()" @current-change="loadRecordList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { FormInstance } from 'element-plus'
|
||||
import { getVerifyRecord, getVerifyTypeList } from '@/app/api/verify'
|
||||
import { img } from '@/utils/common'
|
||||
|
||||
const route = useRoute()
|
||||
const pageName = route.meta.title
|
||||
const router = useRouter()
|
||||
|
||||
const recordTable = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
code: '',
|
||||
type: '',
|
||||
create_time: []
|
||||
}
|
||||
})
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
/**
|
||||
* 获取核销记录列表
|
||||
*/
|
||||
const loadRecordList = (page: number = 1) => {
|
||||
recordTable.loading = true
|
||||
recordTable.page = page
|
||||
|
||||
getVerifyRecord({
|
||||
page: recordTable.page,
|
||||
limit: recordTable.limit,
|
||||
...recordTable.searchParam
|
||||
}).then(res => {
|
||||
recordTable.loading = false
|
||||
recordTable.data = res.data.data
|
||||
recordTable.total = res.data.total
|
||||
}).catch(() => {
|
||||
recordTable.loading = false
|
||||
})
|
||||
}
|
||||
loadRecordList()
|
||||
|
||||
/**
|
||||
* 获取核销类型
|
||||
*/
|
||||
const verifyTypeList = ref<any>([])
|
||||
const setVerifyTypeList = () => {
|
||||
getVerifyTypeList().then(res => {
|
||||
verifyTypeList.value = res.data
|
||||
}).catch()
|
||||
}
|
||||
setVerifyTypeList();
|
||||
|
||||
// 重置
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
loadRecordList()
|
||||
}
|
||||
|
||||
// 详情
|
||||
const detailEvent = (data: any)=>{
|
||||
router.push(`/marketing/verify/detail?code=${data.code}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
125
admin/src/app/views/marketing/verify_detail.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="main-container" v-loading="loading">
|
||||
<div class="detail-head !ml-[20px] !mb-[5px]">
|
||||
<div class="left" @click="router.push({ path: '/marketing/verify' })">
|
||||
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
|
||||
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
|
||||
</div>
|
||||
<span class="adorn">|</span>
|
||||
<span class="right">{{ pageName }}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title">{{ t('核销信息') }}</h3>
|
||||
<div class="flex items-center mt-[15px]">
|
||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('核销类型') }}</span>
|
||||
<span class="text-[14px] text-[#666666]">
|
||||
{{ verifyData.type_name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center mt-[15px]">
|
||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('核销状态') }}</span>
|
||||
<span class="text-[14px] text-[#666666]">
|
||||
已核销
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center mt-[15px]">
|
||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('核销人员') }}</span>
|
||||
<span class="text-[14px] text-[#666666]">
|
||||
{{ verifyData.member ? verifyData.member.nickname : '--' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center mt-[15px]">
|
||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('核销时间') }}</span>
|
||||
<span class="text-[14px] text-[#666666]">
|
||||
{{verifyData.create_time}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center mt-[15px]" v-for="(item,index) in verifyContentData.fixed" :key="index">
|
||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ item.title }}</span>
|
||||
<span class="text-[14px] text-[#666666]">
|
||||
{{ item.value }}
|
||||
</span>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none" shadow="never" v-for="(item,index) in verifyContentData.diy" :key="index">
|
||||
<h3 class="panel-title">{{ item.title }}</h3>
|
||||
<div class="flex items-center mt-[15px]" v-for="(subItem,subIndex) in item.list" :key="subIndex">
|
||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ subItem.title }}</span>
|
||||
<span class="text-[14px] text-[#666666]">
|
||||
{{ subItem.value }}
|
||||
</span>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title">{{ t('商品信息') }}</h3>
|
||||
<el-table :data="verifyGoodsList" size="large">
|
||||
<el-table-column :label="t('商品名称')" align="left" width="300">
|
||||
<template #default="{ row }">
|
||||
<div class="flex">
|
||||
<div class="flex items-center shrink-0">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" :src="img(row.cover)" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<p class="multi-hidden text-[14px]">{{ row.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="num" :label="t('数量')" min-width="50" align="right" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getVerifyDetail } from '@/app/api/verify'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
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 router = useRouter()
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
// 获取核销信息
|
||||
const code: any = route.query.code
|
||||
let verifyData: any = ref({})
|
||||
let verifyContentData: any = ref({})
|
||||
let verifyGoodsList: any = ref([])
|
||||
|
||||
const getVerifyDetailFn = async () => {
|
||||
loading.value = true
|
||||
if (code) {
|
||||
const data = await (await getVerifyDetail(code)).data
|
||||
if (!data || Object.keys(data).length == 0) {
|
||||
ElMessage.error(t('memberNull'))
|
||||
setTimeout(() => {
|
||||
router.go(-1)
|
||||
}, 2000)
|
||||
return false
|
||||
}
|
||||
verifyData.value = data;
|
||||
verifyContentData.value = data.value.content || {}
|
||||
verifyGoodsList.value = data.value.list || []
|
||||
loading.value = false
|
||||
} else {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
getVerifyDetailFn()
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -8,8 +8,7 @@
|
||||
<el-row class="flex">
|
||||
<el-col :span="8" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic
|
||||
:value="balanceStatistics.money && balanceStatistics.balance ? (Number.parseFloat(balanceStatistics.money) + Number.parseFloat(balanceStatistics.balance)).toFixed(2) : '0.00'"></el-statistic>
|
||||
<el-statistic :value="balanceStatistics.money && balanceStatistics.balance ? (Number.parseFloat(balanceStatistics.money) + Number.parseFloat(balanceStatistics.balance)).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('totalAllBalance') }}</span>
|
||||
|
||||
@ -8,8 +8,7 @@
|
||||
<el-row class="flex">
|
||||
<el-col :span="6" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic
|
||||
:value="commissionStatistics.total_commission ? Number.parseFloat(commissionStatistics.total_commission).toFixed(2) : '0.00'"></el-statistic>
|
||||
<el-statistic :value="commissionStatistics.total_commission ? Number.parseFloat(commissionStatistics.total_commission).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('totalCommission') }}</span>
|
||||
@ -19,8 +18,7 @@
|
||||
</el-col>
|
||||
<el-col :span="6" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic
|
||||
:value="commissionStatistics.commission ? Number.parseFloat(commissionStatistics.commission).toFixed(2) : '0.00'"></el-statistic>
|
||||
<el-statistic :value="commissionStatistics.commission ? Number.parseFloat(commissionStatistics.commission).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('commission') }}</span>
|
||||
@ -30,8 +28,7 @@
|
||||
</el-col>
|
||||
<el-col :span="6" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic
|
||||
:value="commissionStatistics.withdrawn_commission ? Number.parseFloat(commissionStatistics.withdrawn_commission).toFixed(2) : '0.00'"></el-statistic>
|
||||
<el-statistic :value="commissionStatistics.withdrawn_commission ? Number.parseFloat(commissionStatistics.withdrawn_commission).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('withdrawnCommission') }}</span>
|
||||
@ -41,8 +38,7 @@
|
||||
</el-col>
|
||||
<el-col :span="6" class="min-w-[100px]">
|
||||
<div class="statistic-card">
|
||||
<el-statistic
|
||||
:value="commissionStatistics.commission_cash_outing ? Number.parseFloat(commissionStatistics.commission_cash_outing).toFixed(2) : '0.00'"></el-statistic>
|
||||
<el-statistic :value="commissionStatistics.commission_cash_outing ? Number.parseFloat(commissionStatistics.commission_cash_outing).toFixed(2) : '0.00'"></el-statistic>
|
||||
<div class="statistic-footer">
|
||||
<div class="footer-item text-[14px] text-[#666]">
|
||||
<span>{{ t('cashOutingCommission') }}</span>
|
||||
|
||||
@ -41,6 +41,7 @@ import { filterNumber } from '@/utils/common'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const loading = ref(false)
|
||||
const repeat = ref(false)
|
||||
let popTitle: string = ''
|
||||
let memberNo: string = ''
|
||||
|
||||
@ -67,7 +68,6 @@ const formRules = computed(() => {
|
||||
{ required: true, message: t('memberNoPlaceholder'), trigger: 'blur' },
|
||||
{ validator: memberNoVerify, trigger: 'blur' }
|
||||
],
|
||||
|
||||
mobile: [
|
||||
{ required: true, message: t('mobilePlaceholder'), trigger: 'blur' },
|
||||
{ validator: mobileVerify, trigger: 'blur' }
|
||||
@ -129,14 +129,19 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
if (repeat.value) return
|
||||
repeat.value = true
|
||||
|
||||
const data = formData
|
||||
|
||||
save(data).then(res => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
100
admin/src/app/views/member/components/benefits-discount.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<el-form-item label="" prop="discount" class="!mb-[10px]">
|
||||
<div>
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="formData.is_use" :true-label="1" :false-label="0" label="" size="large" />
|
||||
<span class="ml-[10px] el-form-item__label">消费折扣</span>
|
||||
<div class="w-[120px]" v-show="formData.is_use">
|
||||
<el-input v-model.trim="formData.discount" clearable >
|
||||
<template #append>折</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-gray-400 mb-[5px]">会员购买产品默认折扣,需要商品设置参与会员折扣有效</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
const formData = ref({
|
||||
is_use: 0,
|
||||
discount: ''
|
||||
})
|
||||
const formRef = ref(null)
|
||||
|
||||
const formRules = reactive<FormRules>({
|
||||
discount: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (formData.value.is_use) {
|
||||
if (Test.empty(formData.value.discount)) {
|
||||
callback('请输入折扣')
|
||||
}
|
||||
if (!Test.decimal(formData.value.discount, 1)) {
|
||||
callback('折扣格式错误')
|
||||
}
|
||||
if (parseFloat(formData.value.discount) < 0.1 || parseFloat(formData.value.discount) > 9.9) {
|
||||
callback('折扣只能输入0.1~9.9之间的值')
|
||||
}
|
||||
if (formData.value.discount <= 0) {
|
||||
callback('折扣不能小于等于0')
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
await formRef.value?.validate((valid) => {
|
||||
verify = valid
|
||||
})
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -31,6 +31,7 @@ import { filterNumber } from '@/utils/common'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const loading = ref(false)
|
||||
const repeat = ref(false)
|
||||
let popTitle:string = ''
|
||||
|
||||
/**
|
||||
@ -41,7 +42,6 @@ const initialFormData = {
|
||||
label_name: '',
|
||||
memo: '',
|
||||
sort: 0
|
||||
|
||||
}
|
||||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||
|
||||
@ -83,14 +83,19 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
if (repeat.value) return
|
||||
repeat.value = true
|
||||
|
||||
const data = formData
|
||||
|
||||
save(data).then(res => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
// showDialog.value = false
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="title || t('updateMember')" width="500px" :destroy-on-close="true">
|
||||
|
||||
<el-form :model="saveData" label-width="90px" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-form :model="saveData" label-width="90px" :rules="formRules" ref="formRef" class="page-form" v-loading="loading">
|
||||
<el-form-item :label="t('headimg')" v-if="type == 'headimg'">
|
||||
<upload-image v-model="saveData.headimg" />
|
||||
</el-form-item>
|
||||
@ -21,6 +21,15 @@
|
||||
<el-option :label="item['label_name']" :value="item['label_id']" v-for="(item,index) in labelSelectData" :key="index"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div v-if="type == 'member_level'">
|
||||
<el-form-item :label="t('memberLevelUpdate')" prop="member_level">
|
||||
<el-select v-model="saveData.member_level" :placeholder="t('memberLevelPlaceholder')" class="input-width">
|
||||
<el-option :label="t('memberLevelPlaceholder')" :value="0" />
|
||||
<el-option :label="item['level_name']" :value="item['level_id']" v-for="(item,index) in levelSelectData" :key="index"/>
|
||||
</el-select>
|
||||
<div class="text-sm text-gray-400">{{ t('memberLevelUpdateTips') }}</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
@ -33,10 +42,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { editMemberDetail, getMemberLabelAll } from '@/app/api/member'
|
||||
import { editMemberDetail, getMemberLabelAll, getMemberLevelAll } from '@/app/api/member'
|
||||
import Test from "@/utils/test";
|
||||
|
||||
// 修改类型
|
||||
const type = ref('')
|
||||
@ -45,6 +55,9 @@ const title = ref('')
|
||||
const memberId = ref('')
|
||||
const showDialog = ref(false)
|
||||
const loading = ref(false)
|
||||
const repeat = ref(false)
|
||||
const formRef = ref(null)
|
||||
|
||||
const sexSelectData = ref([
|
||||
{
|
||||
id: 0,
|
||||
@ -66,6 +79,26 @@ const getMemberLabelAllFn = async () => {
|
||||
}
|
||||
getMemberLabelAllFn()
|
||||
|
||||
const levelSelectData = ref([])
|
||||
getMemberLevelAll().then(({ data }) => {
|
||||
levelSelectData.value = data
|
||||
})
|
||||
|
||||
const formRules = computed(() => {
|
||||
return {
|
||||
member_level: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
if (Test.empty(saveData.member_level)) {
|
||||
callback(t('memberLevelPlaceholder'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
@ -73,6 +106,7 @@ const initialFormData = {
|
||||
headimg: '',
|
||||
nickname: '',
|
||||
member_label: '',
|
||||
member_level: '',
|
||||
sex: '',
|
||||
birthday: ''
|
||||
}
|
||||
@ -85,20 +119,29 @@ const emit = defineEmits(['complete'])
|
||||
* @param formEl
|
||||
*/
|
||||
const confirm = async (formEl: FormInstance | undefined) => {
|
||||
loading.value = true
|
||||
const data = ref({
|
||||
member_id: memberId.value,
|
||||
field: type.value,
|
||||
value: saveData[type.value]
|
||||
})
|
||||
await formRef.value?.validate((valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
editMemberDetail(data.value).then(res => {
|
||||
loading.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
// showDialog.value = false
|
||||
if (repeat.value) return
|
||||
repeat.value = true
|
||||
|
||||
const data = ref({
|
||||
member_id: memberId.value,
|
||||
field: type.value,
|
||||
value: saveData[type.value]
|
||||
})
|
||||
|
||||
editMemberDetail(data.value).then(res => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
93
admin/src/app/views/member/components/gift-balance.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<el-form-item label="" prop="money">
|
||||
<div class="flex items-center">
|
||||
<el-checkbox v-model="formData.is_use" :true-label="1" :false-label="0" label="" size="large" />
|
||||
<span class="ml-[10px] el-form-item__label">送</span>
|
||||
<div class="w-[70px]">
|
||||
<el-input v-model.trim="formData.money" clearable />
|
||||
</div>
|
||||
<span class="ml-[15px] el-form-item__label">元红包</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
const formData = ref({
|
||||
is_use: 0,
|
||||
money: ''
|
||||
})
|
||||
const formRef = ref(null)
|
||||
|
||||
const formRules = reactive<FormRules>({
|
||||
money: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (formData.value.is_use) {
|
||||
if (Test.empty(formData.value.money)) {
|
||||
callback('请输入红包金额')
|
||||
}
|
||||
if (!Test.amount(formData.value.money)) {
|
||||
callback('红包金额格式错误')
|
||||
}
|
||||
if (formData.value.money <= 0) {
|
||||
callback('红包金额不能小于等于0')
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
await formRef.value?.validate((valid) => {
|
||||
verify = valid
|
||||
})
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
92
admin/src/app/views/member/components/gift-point.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<el-form-item label="" prop="num">
|
||||
<el-checkbox v-model="formData.is_use" :true-label="1" :false-label="0" label="" size="large" />
|
||||
<span class="ml-[10px] el-form-item__label">送</span>
|
||||
<div class="w-[70px]">
|
||||
<el-input v-model.trim="formData.num" clearable />
|
||||
</div>
|
||||
<span class="ml-[15px] el-form-item__label">积分</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
const formData = ref({
|
||||
is_use: 0,
|
||||
num: ''
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const formRules = reactive<FormRules>({
|
||||
num: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
if (formData.value.is_use) {
|
||||
if (Test.empty(formData.value.num)) {
|
||||
callback('请输入发放积分数量')
|
||||
}
|
||||
if (!Test.digits(formData.value.num)) {
|
||||
callback('积分数量格式错误')
|
||||
}
|
||||
if (formData.value.num <= 0) {
|
||||
callback('积分数量不能小于等于0')
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
await formRef.value?.validate((valid) => {
|
||||
verify = valid
|
||||
})
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<el-form-item label="" prop="growth">
|
||||
<div>
|
||||
<div>
|
||||
<span class="el-form-item__label">会员注册</span>
|
||||
<el-switch v-model="formData.is_use" :active-value="1" :inactive-value="0"/>
|
||||
</div>
|
||||
<div class="flex mt-[10px]" v-show="formData.is_use">
|
||||
<span class="el-form-item__label">发放</span>
|
||||
<div class="w-[70px]">
|
||||
<el-input v-model.number.trim="formData.growth" clearable />
|
||||
</div>
|
||||
<span class="ml-[10px] el-form-item__label">成长值</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
const formData = ref({
|
||||
is_use: 0,
|
||||
growth: ''
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const formRules = reactive<FormRules>({
|
||||
growth: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
if (formData.value.is_use) {
|
||||
if (Test.empty(formData.value.growth)) {
|
||||
callback('请输入发放成长值数量')
|
||||
}
|
||||
if (!Test.digits(formData.value.growth)) {
|
||||
callback('成长值数量格式错误')
|
||||
}
|
||||
if (formData.value.growth <= 0) {
|
||||
callback('成长值数量不能小于等于0')
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
await formRef.value?.validate((valid) => {
|
||||
verify = valid
|
||||
})
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="t('adjustBalance')" width="550px" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="110px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-form :model="formData" label-width="110px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading" @submit.enter.prevent>
|
||||
|
||||
<el-form-item :label="t('currBalance')" >
|
||||
<div class="input-width"> {{ formData.balance }} </div>
|
||||
@ -40,6 +40,7 @@ import { adjustBalance } from '@/app/api/member'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const loading = ref(true)
|
||||
const repeat = ref(false)
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
@ -91,15 +92,21 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
if (repeat.value) return
|
||||
repeat.value = true
|
||||
|
||||
formData.account_data = Math.abs(parseFloat(formData.adjust)) * formData.adjust_type
|
||||
const data = formData
|
||||
|
||||
adjustBalance(data).then(res => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
// showDialog.value = false
|
||||
})
|
||||
}
|
||||
|
||||
69
admin/src/app/views/member/components/member-benefits.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div v-for="item in benefits">
|
||||
<component :is="item.component" v-model="formData[item.key]" ref="benefitsRefs" v-if="item.component"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineAsyncComponent, computed, watch } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getBenefitsDict } from '@/app/api/member'
|
||||
|
||||
const benefits = ref({})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const formData = ref({})
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
const benefitsRefs = ref([])
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const modules: any = import.meta.glob('@/**/*.vue')
|
||||
getBenefitsDict().then(({ data }) => {
|
||||
Object.keys(data).forEach((key: string) => {
|
||||
data[key].component && (data[key].component = defineAsyncComponent(modules[data[key].component]))
|
||||
})
|
||||
benefits.value = data
|
||||
})
|
||||
|
||||
/**
|
||||
* 验证
|
||||
*/
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
for (let i = 0; i < benefitsRefs.value.length; i++) {
|
||||
const item = benefitsRefs.value[i]
|
||||
!await item.verify() && (verify = false)
|
||||
}
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -1,12 +1,10 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="t('moneyInfo')" width="550px" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="110px" ref="formRef" :rules="formRules" class="page-form"
|
||||
v-loading="loading">
|
||||
<el-form :model="formData" label-width="110px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
|
||||
<el-form-item :label="t('headimg')">
|
||||
<div class="flex items-center">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-if="formData.member.headimg"
|
||||
:src="img(formData.member.headimg)" alt="">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-if="formData.member.headimg" :src="img(formData.member.headimg)" alt="">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-else src="@/app/assets/images/default_headimg.png" alt="">
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
65
admin/src/app/views/member/components/member-gift.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div v-for="item in gifts">
|
||||
<component :is="item.component" v-model="formData[item.key]" ref="giftRefs" v-if="item.component"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineAsyncComponent, computed, watch } from 'vue'
|
||||
import { getGiftDict } from '@/app/api/member'
|
||||
|
||||
const gifts = ref({})
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
const formData = ref({})
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
const giftRefs = ref([])
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const modules: any = import.meta.glob('@/**/*.vue')
|
||||
getGiftDict().then(({ data }) => {
|
||||
Object.keys(data).forEach((key: string) => {
|
||||
data[key].component && (data[key].component = defineAsyncComponent(modules[data[key].component]))
|
||||
})
|
||||
gifts.value = data
|
||||
})
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
for (let i = 0; i < giftRefs.value.length; i++) {
|
||||
const item = giftRefs.value[i]
|
||||
!await item.verify() && (verify = false)
|
||||
}
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@ -68,7 +68,6 @@ const initialFormData = {
|
||||
nickname: '',
|
||||
related_id: '',
|
||||
username: ''
|
||||
|
||||
}
|
||||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-dialog v-model="showDialog" :title="t('adjustPoint')" width="550px" :destroy-on-close="true">
|
||||
<el-form :model="formData" label-width="110px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
|
||||
<el-form :model="formData" label-width="110px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading" @submit.enter.prevent>
|
||||
|
||||
<el-form-item :label="t('currPoint')" >
|
||||
<div class="input-width"> {{ formData.point }} </div>
|
||||
@ -40,6 +40,7 @@ import { adjustPoint } from '@/app/api/member'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const loading = ref(true)
|
||||
const repeat = ref(false)
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
@ -93,15 +94,21 @@ const confirm = async (formEl: FormInstance | undefined) => {
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
|
||||
if (repeat.value) return
|
||||
repeat.value = true
|
||||
|
||||
formData.account_data = Math.abs(parseFloat(formData.adjust)) * formData.adjust_type;
|
||||
const data = formData
|
||||
|
||||
adjustPoint(data).then(res => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
showDialog.value = false
|
||||
emit('complete')
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
repeat.value = false
|
||||
// showDialog.value = false
|
||||
})
|
||||
}
|
||||
|
||||
@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules">
|
||||
<el-form-item label="" prop="point">
|
||||
<div>
|
||||
<div>
|
||||
<span class="el-form-item__label">会员注册</span>
|
||||
<el-switch v-model="formData.is_use" :active-value="1" :inactive-value="0"/>
|
||||
</div>
|
||||
<div class="flex mt-[10px]" v-show="formData.is_use">
|
||||
<span class="el-form-item__label">发放</span>
|
||||
<div class="w-[70px]">
|
||||
<el-input v-model.number.trim="formData.point" clearable />
|
||||
</div>
|
||||
<span class="ml-[10px] el-form-item__label">积分</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
const formData = ref({
|
||||
is_use: 0,
|
||||
point: ''
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const formRules = reactive<FormRules>({
|
||||
point: [
|
||||
{
|
||||
validator: (rule: any, value: any, callback: Function) => {
|
||||
if (formData.value.is_use) {
|
||||
if (Test.empty(formData.value.point)) {
|
||||
callback('请输入发放积分数量')
|
||||
}
|
||||
if (!Test.digits(formData.value.point)) {
|
||||
callback('积分数量格式错误')
|
||||
}
|
||||
if (formData.value.point <= 0) {
|
||||
callback('积分数量不能小于等于0')
|
||||
}
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const value = computed({
|
||||
get () {
|
||||
return props.modelValue
|
||||
},
|
||||
set (value) {
|
||||
emits('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => value.value, (nval, oval) => {
|
||||
if ((!oval || !Object.keys(oval).length) && Object.keys(nval).length) {
|
||||
formData.value = value.value
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => formData.value, () => {
|
||||
value.value = formData.value
|
||||
}, { deep: true })
|
||||
|
||||
const verify = async () => {
|
||||
let verify = true
|
||||
await formRef.value?.validate((valid) => {
|
||||
verify = valid
|
||||
})
|
||||
return verify
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
verify
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
168
admin/src/app/views/member/growth.vue
Normal file
@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<div class="flex justify-between items-center mb-[5px]">
|
||||
<span class="text-page-title">{{ pageName }}</span>
|
||||
</div>
|
||||
<el-card class="box-card !border-none mb-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="memberAccountLogTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('fromType')" prop="from_type">
|
||||
<el-select v-model="memberAccountLogTableData.searchParam.from_type" clearable :placeholder="t('fromTypePlaceholder')" class="input-width">
|
||||
<el-option :label="t('selectPlaceholder')" value="" />
|
||||
<el-option :label="item.name" :value="key" v-for="(item, key) in fromTypeList" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('createTime')" prop="create_time">
|
||||
<el-date-picker v-model="memberAccountLogTableData.searchParam.create_time" type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
|
||||
:end-placeholder="t('endDate')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadMemberAccountLogList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div class="mt-[16px]">
|
||||
<el-table :data="memberAccountLogTableData.data" size="large" v-loading="memberAccountLogTableData.loading">
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !memberAccountLogTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="member_id" :label="t('memberId')" min-width="110" :show-overflow-tooltip="true">
|
||||
<template #default="{ row }">
|
||||
{{ row.member.member_no }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('memberInfo')" min-width="150" :show-overflow-tooltip="true">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center cursor-pointer" @click="toMember(row.member_id)">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-if="row.member.headimg" :src="img(row.member.headimg)" alt="">
|
||||
<img class="w-[50px] h-[50px] mr-[10px]" v-else src="@/app/assets/images/default_headimg.png" alt="">
|
||||
<div class="flex flex flex-col">
|
||||
<span>{{ row.member.nickname || '' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" :label="t('mobile')" min-width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.member.mobile || '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="account_data" :label="t('accountData')" min-width="110" align="right">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.account_data >= 0">+{{ row.account_data }}</span>
|
||||
<span v-else>{{ row.account_data }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="account_sum" :label="t('accountSum')" min-width="120" align="right" />
|
||||
|
||||
<el-table-column prop="from_type_name" :label="t('fromType')" min-width="180" align="center" />
|
||||
<el-table-column prop="memo" :label="t('memo')" min-width="300" align="left" :show-overflow-tooltip="true"/>
|
||||
<el-table-column prop="create_time" :show-overflow-tooltip="true" :label="t('createTime')" min-width="150" />
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="memberAccountLogTableData.page"
|
||||
v-model:page-size="memberAccountLogTableData.limit" layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="memberAccountLogTableData.total" @size-change="loadMemberAccountLogList()"
|
||||
@current-change="loadMemberAccountLogList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<point-info ref="pointDialog" @complete="loadMemberAccountLogList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getChangeTypeList, getGrowthList } from '@/app/api/member'
|
||||
import { FormInstance } from 'element-plus'
|
||||
import { img } from '@/utils/common'
|
||||
import pointInfo from '@/app/views/member/components/member-point-info.vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const memberId: number = parseInt(route.query.id || 0)
|
||||
const pageName = route.meta.title
|
||||
|
||||
const memberAccountLogTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
keywords: '',
|
||||
from_type: '',
|
||||
create_time: '',
|
||||
mobile: '',
|
||||
member_id: memberId
|
||||
}
|
||||
})
|
||||
|
||||
const fromTypeList = ref([])
|
||||
|
||||
const setFromTypeList = async () => {
|
||||
fromTypeList.value = await (await getChangeTypeList('growth')).data
|
||||
}
|
||||
|
||||
setFromTypeList()
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
loadMemberAccountLogList()
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取会员账单表列表
|
||||
*/
|
||||
const loadMemberAccountLogList = (page: number = 1) => {
|
||||
memberAccountLogTableData.loading = true
|
||||
memberAccountLogTableData.page = page
|
||||
|
||||
getGrowthList({
|
||||
page: memberAccountLogTableData.page,
|
||||
limit: memberAccountLogTableData.limit,
|
||||
...memberAccountLogTableData.searchParam
|
||||
}).then(res => {
|
||||
memberAccountLogTableData.loading = false
|
||||
memberAccountLogTableData.data = res.data.data
|
||||
memberAccountLogTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
memberAccountLogTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadMemberAccountLogList()
|
||||
|
||||
const pointDialog: Record<string, any> | null = ref(null)
|
||||
|
||||
/**
|
||||
* 查看详情
|
||||
* @param data
|
||||
*/
|
||||
const infoEvent = (data: any) => {
|
||||
pointDialog.value.setFormData(data)
|
||||
pointDialog.value.showDialog = true
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
/**
|
||||
* 会员详情
|
||||
*/
|
||||
const toMember = (memberId: number) => {
|
||||
router.push(`/member/detail?id=${memberId}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
153
admin/src/app/views/member/level.vue
Normal file
@ -0,0 +1,153 @@
|
||||
<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="addEvent">{{ t('addMemberLevel') }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
|
||||
<el-form :inline="true" :model="memberLevelTableData.searchParam" ref="searchFormRef">
|
||||
<el-form-item :label="t('levelName')" prop="level_name">
|
||||
<el-input v-model.trim="memberLevelTableData.searchParam.level_name" :placeholder="t('levelNamePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadMemberLevelList()">{{ t('search') }}</el-button>
|
||||
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div class="mt-[10px]">
|
||||
<el-table :data="memberLevelTableData.data" size="large" v-loading="memberLevelTableData.loading">
|
||||
|
||||
<template #empty>
|
||||
<span>{{ !memberLevelTableData.loading ? t('emptyData') : '' }}</span>
|
||||
</template>
|
||||
|
||||
<el-table-column prop="level_name" :label="t('levelName')" min-width="120" />
|
||||
<el-table-column prop="growth" :label="t('growth')" min-width="120" />
|
||||
<el-table-column :label="t('levelBenefits')" min-width="120" :show-overflow-tooltip="true">
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<template v-for="item in row.level_benefits">
|
||||
<div v-if="item.content">{{ item.content }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('levelGifts')" min-width="120" >
|
||||
<template #default="{ row }">
|
||||
<div>
|
||||
<template v-for="item in row.level_gifts">
|
||||
<div v-if="item.content">{{ item.content }}</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="member_num" :label="t('memberNumber')" min-width="120" />
|
||||
|
||||
<el-table-column :label="t('operation')" align="right" fixed="right" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
|
||||
<el-button type="primary" link @click="deleteEvent(row.level_id)">{{ t('delete') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="mt-[16px] flex justify-end">
|
||||
<el-pagination v-model:current-page="memberLevelTableData.page" v-model:page-size="memberLevelTableData.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="memberLevelTableData.total"
|
||||
@size-change="loadMemberLevelList()" @current-change="loadMemberLevelList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { getMemberLevelPageList, deleteMemberLevel } from '@/app/api/member'
|
||||
import { ElMessageBox, FormInstance } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const pageName = route.meta.title
|
||||
|
||||
const memberLevelTableData = reactive({
|
||||
page: 1,
|
||||
limit: 10,
|
||||
total: 0,
|
||||
loading: true,
|
||||
data: [],
|
||||
searchParam: {
|
||||
level_name: ''
|
||||
}
|
||||
})
|
||||
|
||||
const searchFormRef = ref<FormInstance>()
|
||||
|
||||
const resetForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
formEl.resetFields()
|
||||
loadMemberLevelList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员标签列表
|
||||
*/
|
||||
const loadMemberLevelList = (page: number = 1) => {
|
||||
memberLevelTableData.loading = true
|
||||
memberLevelTableData.page = page
|
||||
|
||||
getMemberLevelPageList({
|
||||
page: memberLevelTableData.page,
|
||||
limit: memberLevelTableData.limit,
|
||||
...memberLevelTableData.searchParam
|
||||
}).then(res => {
|
||||
memberLevelTableData.loading = false
|
||||
memberLevelTableData.data = res.data.data
|
||||
memberLevelTableData.total = res.data.total
|
||||
}).catch(() => {
|
||||
memberLevelTableData.loading = false
|
||||
})
|
||||
}
|
||||
loadMemberLevelList()
|
||||
|
||||
/**
|
||||
* 添加会员标签
|
||||
*/
|
||||
const addEvent = () => {
|
||||
router.push({ path: '/member/level_edit' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑会员标签
|
||||
* @param data
|
||||
*/
|
||||
const editEvent = (data: any) => {
|
||||
router.push({ path: '/member/level_edit', query: { id: data.level_id } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会员标签
|
||||
*/
|
||||
const deleteEvent = (id: number) => {
|
||||
ElMessageBox.confirm(t('memberLevelDeleteTips'), t('warning'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
deleteMemberLevel(id).then(() => {
|
||||
loadMemberLevelList()
|
||||
}).catch(() => {
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
153
admin/src/app/views/member/level_edit.vue
Normal file
@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
|
||||
<div class="detail-head !ml-[20px] !mb-[5px]">
|
||||
<div class="left" @click="router.push({ path: '/member/level' })">
|
||||
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
|
||||
<span class="ml-[1px]">{{t('returnToPreviousPage')}}</span>
|
||||
</div>
|
||||
<span class="adorn">|</span>
|
||||
<span class="right">{{ pageName }}</span>
|
||||
</div>
|
||||
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" >
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('basicInfo') }}</h3>
|
||||
</el-card>
|
||||
<el-form-item :label="t('levelName')" prop="level_name">
|
||||
<el-input v-model.trim="formData.level_name" :placeholder="t('levelNamePlaceholder')" class="input-width" maxlength="20" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('remark')" prop="remark">
|
||||
<el-input v-model.trim="formData.remark" type="textarea" :placeholder="t('remarkPlaceholder')" class="input-width" clearable rows="4" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('growth')" prop="growth">
|
||||
<div>
|
||||
<div class="w-[150px]">
|
||||
<el-input v-model.number.trim="formData.growth" :placeholder="t('growthPlaceholder')" clearable />
|
||||
</div>
|
||||
<div class="text-sm text-gray-400 mb-[5px]">{{ t('growthTips') }}</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('levelBenefits') }}</h3>
|
||||
<div class="pl-[100px]">
|
||||
<member-benefits ref="benefitsRef" v-model="formData.level_benefits"/>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="box-card !border-none" shadow="never">
|
||||
<h3 class="panel-title !text-sm">{{ t('levelGift') }}</h3>
|
||||
<div class="pl-[100px]">
|
||||
<member-gift ref="giftRef" v-model="formData.level_gifts"/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-card>
|
||||
|
||||
<div class="fixed-footer-wrap">
|
||||
<div class="fixed-footer">
|
||||
<el-button type="primary" :loading="saveLoading" @click="save(formRef)">{{ t('save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import { FormInstance, FormRules } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import memberBenefits from '@/app/views/member/components/member-benefits.vue'
|
||||
import memberGift from '@/app/views/member/components/member-gift.vue'
|
||||
import { getMemberLevelInfo, addMemberLevel, updateMemberLevel, getMemberLevelAll } from '@/app/api/member'
|
||||
import Test from '@/utils/test'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const pageName = route.meta.title
|
||||
const benefitsRef = ref(null)
|
||||
const giftRef = ref(null)
|
||||
const loading = ref(true)
|
||||
const growthInterval = ref({ min: 0, max: 0 })
|
||||
|
||||
const formData = reactive<Record<string, any>>({
|
||||
level_id: 0,
|
||||
level_name: '',
|
||||
remark: '',
|
||||
growth: '',
|
||||
level_benefits: {},
|
||||
level_gifts: {}
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = reactive<FormRules>({
|
||||
level_name: [
|
||||
{ required: true, message: t('levelNamePlaceholder'), trigger: 'blur' }
|
||||
],
|
||||
growth: [
|
||||
{ required: true, message: t('growthPlaceholder'), trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
if (!Test.digits(formData.growth)) {
|
||||
callback(t('growthFormatError'))
|
||||
}
|
||||
if (formData.growth <= 0) {
|
||||
callback(t('growthNeedGt') + 0)
|
||||
}
|
||||
if (growthInterval.value.min && formData.growth <= growthInterval.value.min) {
|
||||
callback(t('growthNeedGt') + growthInterval.value.min)
|
||||
}
|
||||
if (growthInterval.value.max && formData.growth >= growthInterval.value.max) {
|
||||
callback(t('growthNeedLt') + growthInterval.value.max)
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
if (route.query.id) {
|
||||
getMemberLevelInfo(route.query.id).then(({ data }) => {
|
||||
Object.assign(formData, data)
|
||||
getMemberLevelAll().then(({ data }) => {
|
||||
let index = 0
|
||||
data.forEach((item, i) => {
|
||||
item.level_id == formData.level_id && (index = i)
|
||||
})
|
||||
data[ index - 1 ] && (growthInterval.value.min = data[ index - 1 ].growth)
|
||||
data[ index + 1 ] && (growthInterval.value.max = data[ index + 1 ].growth)
|
||||
})
|
||||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
getMemberLevelAll().then(({ data }) => {
|
||||
data[ data.length - 1 ] && (growthInterval.value.min = data[ data.length - 1 ].growth)
|
||||
})
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const saveLoading = ref(false)
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
const save = async (formEl: FormInstance | undefined) => {
|
||||
if (saveLoading.value || !formEl) return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (!await benefitsRef.value?.verify()) return
|
||||
if (!await giftRef.value?.verify()) return
|
||||
|
||||
saveLoading.value = true
|
||||
const save = formData.level_id ? updateMemberLevel : addMemberLevel
|
||||
save(formData).then(() => {
|
||||
router.push({ path: '/member/level' })
|
||||
}).catch(() => {
|
||||
saveLoading.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||