update admin code

This commit is contained in:
全栈小学生 2023-08-12 15:53:05 +08:00
parent dd11de1fe7
commit e827f909aa
44 changed files with 2726 additions and 319 deletions

View File

@ -1,5 +1,5 @@
// Generated by 'unplugin-auto-import'
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
}

11
admin/components.d.ts vendored
View File

@ -18,7 +18,6 @@ declare module '@vue/runtime-core' {
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
@ -34,7 +33,6 @@ declare module '@vue/runtime-core' {
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElFooter: typeof import('element-plus/es')['ElFooter']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
@ -43,20 +41,23 @@ declare module '@vue/runtime-core' {
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElResult: typeof import('element-plus/es')['ElResult']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElStatistic: typeof import('element-plus/es')['ElStatistic']
ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
@ -67,6 +68,7 @@ declare module '@vue/runtime-core' {
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
HeatMap: typeof import('./src/components/heat-map/index.vue')['default']
Icon: typeof import('./src/components/icon/index.vue')['default']
PopoverInput: typeof import('./src/components/popover-input/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
@ -77,6 +79,9 @@ declare module '@vue/runtime-core' {
UploadFile: typeof import('./src/components/upload-file/index.vue')['default']
UploadImage: typeof import('./src/components/upload-image/index.vue')['default']
UploadVideo: typeof import('./src/components/upload-video/index.vue')['default']
Verify: typeof import('./src/components/verifition/Verify.vue')['default']
VerifyPoints: typeof import('./src/components/verifition/Verify/VerifyPoints.vue')['default']
VerifySlide: typeof import('./src/components/verifition/Verify/VerifySlide.vue')['default']
VideoPlayer: typeof import('./src/components/video-player/index.vue')['default']
}
export interface ComponentCustomProperties {

50
admin/src/api/module.ts Normal file
View File

@ -0,0 +1,50 @@
import request from '@/utils/request'
/**
*
*/
export function getAuthinfo() {
return request.get('niucloud/authinfo')
}
/**
*
*/
export function setAuthinfo(params: Record<string, any>) {
return request.post('niucloud/authinfo', params, {showSuccessMessage: true})
}
/**
*
*/
export function getAdminAuthinfo() {
return request.get('niucloud/admin/authinfo')
}
/**
*
* @returns
*/
export function getModule() {
return request.get('niucloud/module')
}
/**
*
* @param params
* @returns
*/
export function getModuleVersion() {
return request.get(`niucloud/module`)
}
/**
*
* @param params
* @returns
*/
export function downloadVersion(addon) {
return request.post(`addon/download/${addon}`, {}, {showSuccessMessage: true})
}

View File

@ -25,7 +25,7 @@ export function getUrl() {
* @returns
*/
export function getRoleList(params: Record<string, any>) {
return request.get('sys/role', {params})
return request.get('sys/role', { params })
}
/**
@ -42,7 +42,7 @@ export function getRoleInfo(roleId: number) {
* @returns
*/
export function addRole(params: Record<string, any>) {
return request.post(`sys/role`, params, {showSuccessMessage: true})
return request.post(`sys/role`, params, { showSuccessMessage: true })
}
/**
@ -50,7 +50,7 @@ export function addRole(params: Record<string, any>) {
* @param params
*/
export function editRole(params: Record<string, any>) {
return request.put(`sys/role/${params.role_id}`, params, {showSuccessMessage: true})
return request.put(`sys/role/${params.role_id}`, params, { showSuccessMessage: true })
}
/**
@ -58,7 +58,7 @@ export function editRole(params: Record<string, any>) {
* @param roleId
*/
export function deleteRole(roleId: number) {
return request.delete(`sys/role/${roleId}`, {showSuccessMessage: true})
return request.delete(`sys/role/${roleId}`, { showSuccessMessage: true })
}
/**
@ -93,7 +93,7 @@ export function getMenuInfo(menu_key: string) {
* @returns
*/
export function addMenu(params: Record<string, any>) {
return request.post('sys/menu', params, {showSuccessMessage: true})
return request.post('sys/menu', params, { showSuccessMessage: true })
}
/**
@ -101,7 +101,7 @@ export function addMenu(params: Record<string, any>) {
* @param params
*/
export function editMenu(params: Record<string, any>) {
return request.put(`sys/menu/${params.menu_key}`, params, {showSuccessMessage: true})
return request.put(`sys/menu/${params.menu_key}`, params, { showSuccessMessage: true })
}
/**
@ -109,7 +109,7 @@ export function editMenu(params: Record<string, any>) {
* @param menu_key
*/
export function deleteMenu(menu_key: string) {
return request.delete(`sys/menu/${menu_key}`, {showSuccessMessage: true})
return request.delete(`sys/menu/${menu_key}`, { showSuccessMessage: true })
}
/***************************************************** 站点菜单 ****************************************************/
@ -147,7 +147,7 @@ export function getWebConfig() {
* @returns
*/
export function setWebsite(params: Record<string, any>) {
return request.put(`sys/config/website`, params, {showSuccessMessage: true})
return request.put(`sys/config/website`, params, { showSuccessMessage: true })
}
/**
@ -172,7 +172,7 @@ export function getService() {
* @returns
*/
export function setCopyright(params: Record<string, any>) {
return request.put(`sys/config/copyright`, params, {showSuccessMessage: true})
return request.put(`sys/config/copyright`, params, { showSuccessMessage: true })
}
/**
@ -181,7 +181,7 @@ export function setCopyright(params: Record<string, any>) {
* @returns
*/
export function getAttachmentCategoryList(params: Record<string, any>) {
return request.get(`sys/attachment/category`, {params})
return request.get(`sys/attachment/category`, { params })
}
/**
@ -189,7 +189,7 @@ export function getAttachmentCategoryList(params: Record<string, any>) {
* @param params
*/
export function addAttachmentCategory(params: Record<string, any>) {
return request.post(`sys/attachment/category`, params, {showSuccessMessage: true})
return request.post(`sys/attachment/category`, params, { showSuccessMessage: true })
}
/**
@ -198,7 +198,7 @@ export function addAttachmentCategory(params: Record<string, any>) {
* @returns
*/
export function editAttachmentCategory(params: Record<string, any>) {
return request.put(`sys/attachment/category/${params.id}`, params, {showSuccessMessage: true})
return request.put(`sys/attachment/category/${params.id}`, params, { showSuccessMessage: true })
}
/**
@ -207,7 +207,7 @@ export function editAttachmentCategory(params: Record<string, any>) {
* @returns
*/
export function deleteAttachmentCategory(id: number) {
return request.delete(`sys/attachment/category/${id}`, {showSuccessMessage: true})
return request.delete(`sys/attachment/category/${id}`, { showSuccessMessage: true })
}
/**
@ -216,7 +216,7 @@ export function deleteAttachmentCategory(id: number) {
* @returns
*/
export function getAttachmentList(params: Record<string, any>) {
return request.get(`sys/attachment`, {params})
return request.get(`sys/attachment`, { params })
}
/**
@ -225,7 +225,7 @@ export function getAttachmentList(params: Record<string, any>) {
* @returns
*/
export function deleteAttachment(params: Record<string, any>) {
return request.delete(`sys/attachment/del`, {data: params, showSuccessMessage: true})
return request.delete(`sys/attachment/del`, { data: params, showSuccessMessage: true })
}
/**
@ -255,7 +255,7 @@ export function getShortcutMenu() {
*
*/
export function setShortcutMenu(params: Record<string, any>) {
return request.put(`sys/config/shortcut_menu`, params, {showSuccessMessage: true})
return request.put(`sys/config/shortcut_menu`, params, { showSuccessMessage: true })
}
/**
@ -264,7 +264,7 @@ export function setShortcutMenu(params: Record<string, any>) {
* @returns
*/
export function getIconCategoryList(params: Record<string, any>) {
return request.get(`sys/attachment/icon_category`, {params})
return request.get(`sys/attachment/icon_category`, { params })
}
/**
@ -273,7 +273,7 @@ export function getIconCategoryList(params: Record<string, any>) {
* @returns
*/
export function getIconList(params: Record<string, any>) {
return request.get(`sys/attachment/icon`, {params})
return request.get(`sys/attachment/icon`, { params })
}
/***************************************************** 地址管理 ****************************************************/
@ -299,14 +299,14 @@ export function getAreatree(level: number = 1) {
*
*/
export function getAddressInfo(params: any) {
return request.get(`sys/area/get_info`, {params})
return request.get(`sys/area/get_info`, { params })
}
/**
*
*/
export function getContraryAddress(params: any) {
return request.get(`sys/area/contrary`, {params})
return request.get(`sys/area/contrary`, { params })
}
/***************************************************** 存储设置 ****************************************************/
@ -332,7 +332,7 @@ export function getStorageInfo(type: string) {
* @returns
*/
export function editStorage(params: Record<string, any>) {
return request.put(`sys/storage/${params.storage_type}`, params, {showSuccessMessage: true})
return request.put(`sys/storage/${params.storage_type}`, params, { showSuccessMessage: true })
}
/***************************************************** 支付设置 ****************************************************/
@ -350,7 +350,7 @@ export function getPayConfig(type: string) {
* @returns
*/
export function setPayConfig(params: Record<string, any>) {
return request.put(`pay/config/${params.type}`, params, {showSuccessMessage: true});
return request.put(`pay/config/${params.type}`, params, { showSuccessMessage: true });
}
/**
@ -386,7 +386,7 @@ export function setTransferInfo(params: Record<string, any>) {
* @returns
*/
export function getCronList(params: any) {
return request.get(`sys/schedule/list`, {params})
return request.get(`sys/schedule/list`, { params })
}
/**
@ -426,7 +426,7 @@ export function getWeek() {
* @returns
*/
export function addCron(params: Record<string, any>) {
return request.post(`sys/schedule`, params, {showSuccessMessage: true})
return request.post(`sys/schedule`, params, { showSuccessMessage: true })
}
/**
@ -434,7 +434,7 @@ export function addCron(params: Record<string, any>) {
* @returns
*/
export function editCron(params: Record<string, any>) {
return request.put(`sys/schedule/${params.id}`, params, {showSuccessMessage: true})
return request.put(`sys/schedule/${params.id}`, params, { showSuccessMessage: true })
}
/**
@ -442,7 +442,7 @@ export function editCron(params: Record<string, any>) {
* @returns
*/
export function deleteCron(id: string) {
return request.delete(`sys/schedule/${id}`, {showSuccessMessage: true})
return request.delete(`sys/schedule/${id}`, { showSuccessMessage: true })
}
/***************************************************** 协议管理 ****************************************************/
@ -468,7 +468,7 @@ export function getAgreementInfo(key: string) {
* @returns
*/
export function editAgreement(params: Record<string, any>) {
return request.put(`sys/agreement/${params.key}`, params, {showSuccessMessage: true})
return request.put(`sys/agreement/${params.key}`, params, { showSuccessMessage: true })
}
/**
@ -503,7 +503,7 @@ export function getConfigLogin() {
* @returns
*/
export function setConfigLogin(params: Record<string, any>) {
return request.put(`sys/config/login`, params, {showSuccessMessage: true})
return request.put(`sys/config/login`, params, { showSuccessMessage: true })
}
/**
@ -517,7 +517,7 @@ export function getPayConfigList() {
*
*/
export function setPatConfig(params: Record<string, any>) {
return request.post(`pay/channel/set/all`, params, {showSuccessMessage: true})
return request.post(`pay/channel/set/all`, params, { showSuccessMessage: true })
}
@ -526,14 +526,14 @@ export function setPatConfig(params: Record<string, any>) {
*
*/
export function menuRefresh(params: Record<string, any>) {
return request.post(`sys/menu/refresh`, {}, {showSuccessMessage: true})
return request.post(`sys/menu/refresh`, {}, { showSuccessMessage: true })
}
/**
*
*/
export function clearSchemaCache(params: Record<string, any>) {
return request.post(`sys/schema/clear`, {}, {showSuccessMessage: true})
return request.post(`sys/schema/clear`, {}, { showSuccessMessage: true })
}
@ -551,7 +551,7 @@ export function getAppMange() {
* key
*/
export function setMap(params: Record<string, any>) {
return request.put(`sys/config/map`, params, {showSuccessMessage: true})
return request.put(`sys/config/map`, params, { showSuccessMessage: true })
}
/**
@ -573,5 +573,51 @@ export function getIndexList() {
*
*/
export function setIndexList(params: Record<string, any>) {
return request.put(`sys/config/site_index`, params, {showSuccessMessage: true})
return request.put(`sys/config/site_index`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getLayouts() {
return request.get('sys/layout')
}
/**
*
* @returns
*/
export function setLayout(key: string) {
return request.put('sys/layout', { key }, { showSuccessMessage: true })
}
/**
*
*/
export function getPayAuditList(params: Record<string, any>) {
return request.get('pay/audit', { params })
}
/**
*
* @returns
*/
export function payAuditPass(outTradeNo: string) {
return request.put(`pay/pass/${outTradeNo}`, {}, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function payAuditRefuse(params: Record<string, any>) {
return request.put(`pay/refuse/${params.out_trade_no}`, params, { showSuccessMessage: true })
}
/**
*
*/
export function getPayDetail(id: number) {
return request.get(`pay/detail/${id}`)
}

View File

@ -51,7 +51,7 @@ export function uploadVersion(params: Record<string, any>) {
* @returns
*/
export function addVersion(params: Record<string, any>) {
return request.put('applet/version/add', params, {showSuccessMessage: true})
return request.post('applet/version', params, {showSuccessMessage: true})
}
/**
@ -78,7 +78,7 @@ export function getVersionInfo(id: string) {
* @returns
*/
export function editVersion(params: Record<string, any>) {
return request.get(`applet/version/${params.id}`, params, {showSuccessMessage: true})
return request.put(`applet/version/${params.id}`, params, {showSuccessMessage: true})
}
/**
@ -96,5 +96,5 @@ export function deleteVersion(id: string) {
* @returns
*/
export function versionDown(id: string) {
return request.post(`applet/version/download/${id}`)
return request.get(`applet/version/download/${id}`, { "responseType": "blob" })
}

View File

@ -0,0 +1,684 @@
<template>
<div>
<div @click="show">
<slot>
<div v-if="value.heatMapData.length">{{ t('selected') }}<span class="text-primary p-[4px]">{{ value.heatMapData.length}}</span>{{ t('selectedAfterHotArea') }}</div>
<div v-else>{{ t('addHotArea') }}</div>
</slot>
</div>
<el-dialog v-model="showDialog" :title="t('hotAreaSet')" width="45%" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<div class="flex">
<div class="content-box relative bg-cover bg-gray-100 border border-dashed border-gray-500" :style="{ backgroundImage : 'url(' + img(value.imageUrl) + ')',width : contentBoxWidth + 'px', height : contentBoxHeight + 'px' }">
<div v-for="(item,index) in dragBoxArr" :id="'box_' + index" class="area-box cursor-move border border-solid border-[#ccc] w-[100px] h-[100px] absolute top-0 left-0 select-none p-[5px]" :style="{ left : item.left + item.unit, top : item.top + item.unit }" @mousedown="mouseDown($event,index)">
<span>{{ index + 1 }}</span>
<template v-if="item.link.title">
<span class="p-[4px]">|</span>
<span>{{ item.link.title }}</span>
</template>
<span class="box1" @mousedown.stop="resizeMouseDown($event,index)"></span>
<span class="box2" @mousedown.stop="resizeMouseDown($event,index)"></span>
<span class="box3" @mousedown.stop="resizeMouseDown($event,index)"></span>
<span class="box4" @mousedown.stop="resizeMouseDown($event,index)"></span>
</div>
</div>
<el-form label-width="80px" class="pl-[20px]">
<h3 class="mb-[10px] text-lg text-black">{{ t('hotAreaManage') }}</h3>
<el-button type="primary" plain size="small" class="mb-[10px]" @click="addArea">{{ t('addHotArea') }}</el-button>
<div class="overflow-y-auto h-[300px]">
<template v-for="(item,index) in dragBoxArr">
<div class="mb-[16px]" v-if="item">
<el-form-item :label="t('hotArea') + (index + 1)">
<div class="flex items-center">
<diy-link v-model="item.link"/>
<icon class="del cursor-pointer mx-[10px]" name="element-CircleCloseFilled" color="#bbb" size="20px" @click="dragBoxArr.splice(index,1)"/>
</div>
</el-form-item>
</div>
</template>
</div>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel')}}</el-button>
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import {t} from '@/lang'
import {ref, reactive, computed} from 'vue'
import {ElMessage} from 'element-plus'
import {img} from '@/utils/common'
const prop = defineProps({
modelValue: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:modelValue']);
const value: any = computed({
get() {
return prop.modelValue
},
set(value) {
emit('update:modelValue', value)
}
});
const showDialog = ref(false);
const contentBoxWidth = ref(400);
const contentBoxHeight = ref(400);
const num = ref(4);//
const dragBoxArr: any = reactive([]);
//
const addArea = () => {
let left = dragBoxArr.length % num.value * 100;
let top = Math.floor(dragBoxArr.length / num.value) * 100;
if (top >= contentBoxWidth.value) {
top = 0;
left = 0;
}
dragBoxArr.push({
left: left,
top: top,
width: 100,
height: 100,
unit: 'px',
link: {
name: ''
}
});
};
//
const mouseDown = (e: any, index: number) => {
let box: any = document.getElementById('box_' + index);
let disX = e.clientX - box.offsetLeft;
let disY = e.clientY - box.offsetTop;
//
document.onmousemove = function (e) {
box.style.left = e.clientX - disX + 'px';
box.style.top = e.clientY - disY + 'px';
//
if (e.clientX - disX < 0) {
box.style.left = 0;
}
if (e.clientX - disX > contentBoxWidth.value - box.offsetWidth) {
box.style.left = contentBoxWidth.value - box.offsetWidth + 'px';
}
if (e.clientY - disY < 0) {
box.style.top = 0;
}
if (e.clientY - disY > contentBoxHeight.value - box.offsetHeight) {
box.style.top = contentBoxHeight.value - box.offsetHeight + 'px';
}
};
//
document.onmouseup = function (e) {
document.onmousemove = null;
}
};
//
const resizeMouseDown = (e: any, index: number) => {
var oEv = e;
oEv.stopPropagation();
let box: any = document.getElementById('box_' + index);
let className = e.target.className;
//
var oldWidth = box.offsetWidth;
var oldHeight = box.offsetHeight;
// lefttop
var oldX = oEv.clientX;
var oldY = oEv.clientY;
//
var oldLeft = box.offsetLeft;
var oldTop = box.offsetTop;
//
var minWidth = 50;
var minHeight = 50;
document.onmousemove = function (e) {
var oEv = e;
// console.log('move', "width" + oldWidth,
// 'oldLeft: ' + oldLeft, 'oldTop: ' + oldTop,
// 'oldXclientX-- ' + oldX + '' + oEv.clientX,
// 'oldYclientY-- ' + oldY + '' + oEv.clientY,
// )
//
if (className == "box1") {
let width = oldWidth - (oEv.clientX - oldX);
let maxWidth = contentBoxWidth.value;
let height = oldHeight - (oEv.clientY - oldY);
let maxHeight = contentBoxHeight.value - oldTop;
let left = oldLeft + (oEv.clientX - oldX);
let top = oldTop + (oEv.clientY - oldY);
if (width < minWidth) {
width = minWidth
}
if (width > maxWidth) {
width = maxWidth
}
if (height < minHeight) {
height = minHeight
}
if (height > maxHeight) {
height = maxHeight
}
if (oldLeft == 0 && oldTop == 0) {
// left = 0top = 0
if (width == minWidth && height == minHeight) {
// = left = top =
left = minWidth;
top = minHeight
} else if (width == minWidth && height > minHeight) {
// = > left = top =
left = minWidth;
} else if (width > minWidth && height == minHeight) {
// > = left = top =
top = minHeight
} else if (width > minWidth && height > minHeight) {
// > > left = top =
}
} else if (oldLeft == 0 && oldTop > 0) {
// left = 0top > 0
if (width == minWidth && height == minHeight) {
// = left = top =
left = minWidth;
top = box.offsetTop
} else if (width == minWidth && height > minHeight) {
// = > left = top =
left = minWidth;
top = box.offsetTop;
} else if (width > minWidth && height == minHeight) {
// > = left = top =
top = box.offsetTop;
} else if (width > minWidth && height > minHeight) {
// > > left = top =
}
} else if (oldLeft > 0 && oldTop == 0) {
// left > 0top = 0
if (width == minWidth && height == minHeight) {
// = left = top =
left = box.offsetLeft;
top = box.offsetTop;
} else if (width == minWidth && height > minHeight) {
// = > left = top = 0
left = box.offsetLeft;
top = 0;
} else if (width > minWidth && height == minHeight) {
// > = left = top =
top = box.offsetTop;
} else if (width > minWidth && height > minHeight) {
// > > left = top =
}
} else if (oldLeft > 0 && oldTop > 0) {
// left > 0top > 0
if (width == minWidth && height == minHeight) {
// = left = top =
left = box.offsetLeft;
top = box.offsetTop
} else if (width == minWidth && height > minHeight) {
// = > left = top =
left = box.offsetLeft;
top = box.offsetTop;
} else if (width > minWidth && height == minHeight) {
// > = left = top =
top = box.offsetTop;
} else if (width > minWidth && height > minHeight) {
// > > left = top =
}
}
//
if (left < 0) {
left = 0;
width = oldWidth - (oEv.clientX - oldX) + (oldLeft + (oEv.clientX - oldX));
}
//
if (top < 0) {
top = 0;
height = oldTop + (oEv.clientY - oldY) + (oldHeight - (oEv.clientY - oldY));
}
box.style.width = width + 'px';
box.style.height = height + 'px';
box.style.left = left + 'px';
box.style.top = top + 'px';
} else if (className == "box2") {
//
let width = oldWidth + (oEv.clientX - oldX);
let maxWidth = contentBoxWidth.value - oldLeft;
let height = oldHeight - (oEv.clientY - oldY);
let maxHeight = contentBoxHeight.value - oldTop;
let top = oldTop + (oEv.clientY - oldY);
if (width < minWidth) {
width = minWidth
}
if (width > maxWidth) {
width = maxWidth
}
if (height < minHeight) {
height = minHeight
}
if (height > maxHeight) {
height = maxHeight
}
if (oldLeft == 0 && oldTop == 0) {
// left = 0top = 0
if (width == minWidth && height == minHeight) {
// = top =
top = minHeight
} else if (width == minWidth && height > minHeight) {
// = >
} else if (width > minWidth && height == minHeight) {
// > = top =
top = minHeight
} else if (width > minWidth && height > minHeight) {
// > >
}
} else if (oldLeft == 0 && oldTop > 0) {
// left = 0top > 0
if (width == minWidth && height == minHeight) {
// = top =
top = box.offsetTop
} else if (width == minWidth && height > minHeight) {
// = > top =
top = box.offsetTop
} else if (width > minWidth && height == minHeight) {
// > = top =
top = box.offsetTop
} else if (width > minWidth && height > minHeight) {
// > >
}
} else if (oldLeft > 0 && oldTop == 0) {
// left = 0top = 0
if (width == minWidth && height == minHeight) {
// = top =
top = box.offsetTop
} else if (width == minWidth && height > minHeight) {
// = > top = 0
top = 0
} else if (width > minWidth && height == minHeight) {
// > = top =
top = box.offsetTop
} else if (width > minWidth && height > minHeight) {
// > >
}
} else if (oldLeft > 0 && oldTop > 0) {
// left > 0top > 0
if (width == minWidth && height == minHeight) {
// = top =
top = box.offsetTop
} else if (width == minWidth && height > minHeight) {
// = > top =
top = box.offsetTop
} else if (width > minWidth && height == minHeight) {
// > = top =
top = box.offsetTop
} else if (width > minWidth && height > minHeight) {
// > >
}
}
//
if (top < 0) {
top = 0;
height = oldTop + (oEv.clientY - oldY) + (oldHeight - (oEv.clientY - oldY))
}
box.style.width = width + 'px';
box.style.height = height + 'px';
box.style.top = top + 'px';
} else if (className == "box3") {
//
let width = oldWidth - (oEv.clientX - oldX);
let maxWidth = contentBoxWidth.value;
let height = oldHeight + (oEv.clientY - oldY);
let maxHeight = contentBoxHeight.value - oldTop;
let left = oldLeft + (oEv.clientX - oldX);
if (width < minWidth) {
width = minWidth
}
if (width > maxWidth) {
width = maxWidth
}
if (height < minHeight) {
height = minHeight
}
if (height > maxHeight) {
height = maxHeight
}
if (oldLeft == 0 && oldTop == 0) {
// left = 0top = 0
if (width == minWidth && height == minHeight) {
// = left =
left = minWidth;
} else if (width == minWidth && height > minHeight) {
// = > left =
left = minWidth;
} else if (width > minWidth && height == minHeight) {
// > =
} else if (width > minWidth && height > minHeight) {
// > >
}
} else if (oldLeft == 0 && oldTop > 0) {
// left = 0top > 0
if (width == minWidth && height == minHeight) {
// = left =
left = minWidth;
} else if (width == minWidth && height > minHeight) {
// = > left =
left = minWidth;
} else if (width > minWidth && height == minHeight) {
// > =
} else if (width > minWidth && height > minHeight) {
// > >
}
} else if (oldLeft > 0 && oldTop == 0) {
// left > 0top = 0
if (width == minWidth && height == minHeight) {
// = left =
left = box.offsetLeft;
} else if (width == minWidth && height > minHeight) {
// = > left =
left = box.offsetLeft;
} else if (width > minWidth && height == minHeight) {
// > =
} else if (width > minWidth && height > minHeight) {
// > >
}
} else if (oldLeft > 0 && oldTop > 0) {
// left > 0top > 0
if (width == minWidth && height == minHeight) {
// = left =
left = box.offsetLeft;
} else if (width == minWidth && height > minHeight) {
// = > left =
left = box.offsetLeft;
} else if (width > minWidth && height == minHeight) {
// > =
} else if (width > minWidth && height > minHeight) {
// > >
}
}
if (left < 0) {
left = 0;
width = oldWidth - (oEv.clientX - oldX) + (oldLeft + (oEv.clientX - oldX));
}
box.style.width = width + 'px';
box.style.height = height + 'px';
box.style.left = left + 'px';
} else if (className == "box4") {
//
let width = oldWidth + (oEv.clientX - oldX);
let maxWidth = contentBoxWidth.value - oldLeft;
let height = oldHeight + (oEv.clientY - oldY);
let maxHeight = contentBoxHeight.value - oldTop;
if (width < minWidth) {
width = minWidth
}
if (width > maxWidth) {
width = maxWidth
}
if (height < minHeight) {
height = minHeight
}
if (height > maxHeight) {
height = maxHeight
}
box.style.width = width + 'px';
box.style.height = height + 'px';
}
dragBoxArr[index].unit = 'px'
};
//
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
}
};
const show = () => {
//
if (!value.value.imageUrl) {
ElMessage({
type: 'warning',
message: `${t('imageUrlTip')}`,
});
return;
}
if (Object.keys(value.value.heatMapData).length) {
dragBoxArr.splice(0, dragBoxArr.length, ...value.value.heatMapData);
} else {
dragBoxArr.splice(0, dragBoxArr.length);
addArea();
}
showDialog.value = true
};
const save = () => {
var isOk = true;
for (let i = 0; i < dragBoxArr.length; i++) {
if (!dragBoxArr[i].link.title) {
ElMessage({
type: 'warning',
message: t('selectedHotArea') + (i + 1) + t('hotAreaLink'),
});
isOk = false;
break;
}
}
if (!isOk) return;
dragBoxArr.forEach((item: any, index: number) => {
var box: any = document.getElementById('box_' + index);
item.width = parseFloat(box.offsetWidth / contentBoxWidth.value * 100).toFixed(2);
item.height = parseFloat(box.offsetHeight / contentBoxHeight.value * 100).toFixed(2);
item.left = parseFloat(box.offsetLeft / contentBoxWidth.value * 100).toFixed(2);
item.top = parseFloat(box.offsetTop / contentBoxHeight.value * 100).toFixed(2);
item.unit = '%';
});
value.value.heatMapData = dragBoxArr;
showDialog.value = false
};
defineExpose({
showDialog
})
</script>
<style lang="scss" scoped>
.area-box {
background-color: rgba(255, 255, 255, 0.7);
}
.box1, .box2, .box3, .box4 {
width: 10px;
height: 10px;
background-color: #fff;
position: absolute;
border-radius: 50%;
border: 1px solid #333;
}
.box1 {
top: -5px;
left: -5px;
cursor: nw-resize;
}
.box2 {
top: -5px;
right: -5px;
cursor: ne-resize;
}
.box3 {
left: -5px;
bottom: -5px;
cursor: sw-resize;
}
.box4 {
bottom: -5px;
right: -5px;
cursor: se-resize;
}
</style>

View File

@ -0,0 +1,14 @@
{
"companyName": "授权主体",
"siteAddress": "授权域名",
"contactName": "授权联系人",
"authCode": "授权码",
"authSecret": "授权秘钥",
"createTime": "授权时间",
"expireTime": "到期时间",
"authApp": "授权应用",
"authAppKey": "应用标识",
"siteAddressTips": "授权域名不匹配",
"authCodePlaceholder": "请输入核销码",
"authSecretPlaceholder": "请输入授权秘钥"
}

View File

@ -28,5 +28,10 @@
"jobError": "任务队列未启动 请在服务端源码部署目录打开终端执行 php think queue:listen",
"conflictFiles": "冲突文件",
"process": "启动进程",
"open": "开启"
"open": "开启",
"down": "下载",
"addonVersion": "插件版本",
"versionCode": "版本号",
"createTime": "发布时间",
"buyLabel": "已购买"
}

View File

@ -16,5 +16,7 @@
"codeDownTwoDesc": "上传代码",
"codeDownTwoTips": "下载完成之后,使用微信开发工具进行上传",
"codeDownThreeDesc": "发布小程序",
"codeDownThreeTips": "上传之后提交审核,审核通过发布小程序"
"codeDownThreeTips": "上传之后提交审核,审核通过发布小程序",
"weappVersion": "小程序版本",
"close": "关闭"
}

View File

@ -76,7 +76,8 @@
"themeColor": "主题颜色",
"detectionLoginOperation": "确定",
"detectionLoginContent": "已检测到有其他账号登录,需要刷新后才能继续操作。",
"detectionLoginTip": "提示"
"detectionLoginTip": "提示",
"layoutStyle": "布局风格"
},
"axios": {
"unknownError": "未知错误",

View File

@ -109,5 +109,15 @@
"more": "文字",
"morePlaceholder": "请输入文字",
"moreIsShow": "是否显示",
"memberStyle": "会员样式"
"memberStyle": "会员样式",
"template": "模板",
"imageGap": "图片间隙",
"rubikCubeStyle": "魔方样式",
"hotArea": "热区",
"hotAreaSet": "热区设置",
"addHotArea": "添加热区",
"selectedAfterHotArea": "个热区",
"hotAreaManage": "热区管理",
"selectedHotArea": "请选择热区",
"hotAreaLink": "的链接地址"
}

View File

@ -109,5 +109,15 @@
"more": "文字",
"morePlaceholder": "请输入文字",
"moreIsShow": "是否显示",
"memberStyle": "会员样式"
"memberStyle": "会员样式",
"template": "模板",
"imageGap": "图片间隙",
"rubikCubeStyle": "魔方样式",
"hotArea": "热区",
"hotAreaSet": "热区设置",
"addHotArea": "添加热区",
"selectedAfterHotArea": "个热区",
"hotAreaManage": "热区管理",
"selectedHotArea": "请选择热区",
"hotAreaLink": "的链接地址"
}

View File

@ -0,0 +1,18 @@
{
"money":"支付金额",
"outTradeNo": "交易流水号",
"voucher": "支付凭证",
"body": "支付内容",
"pass": "通过",
"refuse": "拒绝",
"refuseReason": "拒绝原因",
"passTips": "确认要通过该支付单据吗?",
"startDate": "开始时间",
"endDate": "结束时间",
"outTradeNoPlaceholder":"请输入交易流水号",
"detail": "详情",
"waitAudit": "待审核",
"passed": "已通过",
"notPass": "未通过",
"all": "全部"
}

View File

@ -0,0 +1,18 @@
{
"pass": "通过",
"refuse": "拒绝",
"refuseReason": "拒绝原因",
"passTips": "确认要通过该支付单据吗?",
"outTradeNo": "交易单号",
"createTime": "交易时间",
"money": "交易金额",
"body": "交易内容",
"channel": "支付场景",
"payStatus": "支付状态",
"payType": "支付方式",
"payTime": "支付时间",
"failTime": "失败时间",
"failReason": "失败原因",
"voucher": "支付凭证",
"auditVoucher": "审核支付凭证"
}

View File

@ -9,6 +9,7 @@
"config": "设置",
"updateWechat": "微信支付",
"updateAlipay": "支付宝支付",
"updateOfflinepay": "线下支付",
"mchId": "商户号",
"mchIdPlaceholder": "请输入商户号",
"mchIdTips": "微信支付商户号MCHID",
@ -48,6 +49,13 @@
"setConfig": "设置支付配置",
"open": "已开启",
"notOpen": "未开启",
"cancel": "取消"
"cancel": "取消",
"collectionName": "收款账户名称",
"collectionBank": "收款银行",
"collectionAccount": "收款账号",
"collectionDesc": "转账说明",
"collectionNamePlaceholder": "请输入收款账户名称",
"collectionBankPlaceholder": "请输入收款银行",
"collectionAccountPlaceholder": "请输入收款账号",
"collectionDescPlaceholder": "请输入转账说明"
}

View File

@ -3,7 +3,7 @@
"contactAddress":"联系地址",
"siteName": "站点名称",
"keywords": "网站关键字",
"logo": "长方形logo",
"logo": "长方形Logo",
"desc": "网站简介",
"province": "省",
"city": "市",
@ -24,7 +24,7 @@
"frontEndName": "前台名称",
"frontEndNamePlaceholder": "请输入前台名称",
"frontEndLogo": "前台Logo",
"icon": "网站方形logo",
"icon": "正方形Logo",
"serviceInformation": "服务信息",
"wechatCode": "公众号二维码",
"customerServiceCode": "客服二维码",

View File

@ -7,5 +7,6 @@
"editVersion": "添加/编辑版本",
"file": "文件",
"filePlaceholder":"请上传版本文件",
"desc": "版本说明"
"desc": "版本说明",
"weappVersionDeleteTips": "确定要删除小程序版本吗"
}

View File

@ -21,10 +21,13 @@
"cronTemplate": "任务模版",
"cronTime": "任务周期",
"openStatus": "任务状态",
"isopen": "是否启用",
"day": "日",
"hour": "时",
"min": "分",
"cronDeleteTips": "你确定要删除任务吗",
"addCron": "添加任务",
"cronTimeTips": "任务周期时间不能为空"
"cronTimeTips": "任务周期时间不能为空",
"cronTipsOne": "启动计划任务方式:",
"cronTipsTwo": "1、使用命令启动php think cron:schedule 如果更改了任务周期、状态、删除任务等操作后,需要重新启动下 php think cron:schedule 确保生效"
}

View File

@ -32,6 +32,21 @@
<el-color-picker v-model="theme" />
</div>
</div>
<!-- 布局风格 -->
<div class="setting-item mb-[10px]">
<div class="title text-base text-tx-secondary">{{ t('layout.layoutStyle') }}</div>
<div class="flex mt-[10px] layout-style flex-wrap">
<div class="relative w-[125px] h-[100px] border mr-[10px] mb-[10px] hover:border-primary"
:class="{ 'border-primary': currLayout == item.key }" v-for="(item, index) in layouts"
@click="handleSetLayout(item.key)">
<div
class="absolute z-1 w-[50px] h-[50px] border border-primary-light-5 rounded-[50%] top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] flex items-center justify-center text-base text-primary-light-5">
{{ item.name }}
</div>
<img :src="img(item.image)" alt="" class="w-full h-full">
</div>
</div>
</div>
</el-scrollbar>
</el-drawer>
</div>
@ -41,11 +56,19 @@
import { ref, computed } from 'vue'
import useSystemStore from '@/stores/modules/system'
import { useDark, useToggle } from '@vueuse/core'
import { setThemeColor } from '@/utils/common'
import { setThemeColor, img } from '@/utils/common'
import { t } from '@/lang'
import { getLayouts, setLayout } from '@/api/sys'
import Storage from '@/utils/storage'
const drawer = ref(false)
const systemStore = useSystemStore()
const layouts = ref([])
const currLayout = ref(Storage.get('layout') || 'default')
getLayouts().then(res => {
layouts.value = res.data
}).catch(() => { })
const isDark = useDark()
const toggleDark = useToggle(isDark)
@ -71,7 +94,6 @@ const sidebar = computed({
}
})
const theme = computed({
get() {
return systemStore.theme
@ -81,10 +103,23 @@ const theme = computed({
setThemeColor(systemStore.theme, systemStore.dark ? 'dark' : 'light')
}
})
const handleSetLayout = (key: string) => {
setLayout(key).then(() => {
Storage.set({ key: 'layout', data: key })
location.reload()
}).catch(() => { })
}
</script>
<style lang="scss" scoped>
:deep(.el-drawer__header) {
margin-bottom: 0 !important;
}
.layout-style {
&>div:nth-child(2n+2) {
margin-right: 0;
}
}
</style>

View File

@ -0,0 +1,21 @@
<template>
<el-container class="w-screen h-screen">
<el-header>
<layout-header />
</el-header>
<el-main>
<router-view></router-view>
</el-main>
<el-footer>
<div class="w-full h-full bg-[#f7f7f7] flex items-center justify-center">
Footer
</div>
</el-footer>
</el-container>
</template>
<script lang="ts" setup>
import layoutHeader from '@/layout/default/components/header/index.vue'
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,18 @@
<template>
<component :is="layout" />
</template>
<script lang="ts" setup>
import { ref, markRaw, defineAsyncComponent } from 'vue'
import Storage from '@/utils/storage'
const modules = import.meta.glob('./*/index.vue')
const siteLayout = Storage.get('layout') || 'default'
const layout = ref<any>(null)
Object.keys(modules).forEach(key => {
key.indexOf(siteLayout) !== -1 && (layout.value = markRaw(defineAsyncComponent(modules[key])))
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,5 +1,5 @@
import { RouteRecordRaw, RouterView } from 'vue-router'
import Default from '@/layout/default/index.vue'
import Default from '@/layout/index.vue'
import Decorate from '@/layout/decorate/index.vue'
// 静态路由

View File

@ -2,7 +2,7 @@ import {defineStore} from 'pinia'
import {t} from '@/lang'
import {toRaw} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {cloneDeep } from 'lodash-es'
import {cloneDeep} from 'lodash-es'
const useDiyStore = defineStore('diy', {
state: () => {
@ -11,11 +11,12 @@ const useDiyStore = defineStore('diy', {
load: false, // 加载状态
currentIndex: -99, // 当前正在编辑的组件下标
currentComponent: 'edit-page', // 当前正在编辑的组件名称
pageMode: 'diy',
editTab: 'content',// 编辑页面
name: '', // 页面标识
type: '', // 页面模板
typeName: '', // 页面模板名称
templateName : '', // 页面模板标识
templateName: '', // 页面模板标识
isDefault: 0, // 是否默认页面
predefineColors: [
'#F4391c',
@ -204,6 +205,7 @@ const useDiyStore = defineStore('diy', {
// 将数据发送到uniapp
postMessage() {
var diyData = JSON.stringify({
pageMode: this.pageMode,
currentIndex: this.currentIndex,
global: toRaw(this.global),
value: toRaw(this.value)

View File

@ -37,6 +37,7 @@ const useSystemStore = defineStore('user', {
storage.set({ key: 'siteInfo', data: res.data.site_info })
storage.set({ key: 'comparisonSiteIdStorage', data: res.data.site_info.site_id })
storage.set({ key: 'comparisonTokenStorage', data: res.data.token })
storage.set({ key: 'layout', data: (res.data.layout || 'default') })
resolve(res)
})
.catch((error) => {

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 3883393 */
src: url('//at.alicdn.com/t/c/font_3883393_0wpbzuop0spi.woff2?t=1688032173237') format('woff2'),
url('//at.alicdn.com/t/c/font_3883393_0wpbzuop0spi.woff?t=1688032173237') format('woff'),
url('//at.alicdn.com/t/c/font_3883393_0wpbzuop0spi.ttf?t=1688032173237') format('truetype');
src: url('//at.alicdn.com/t/c/font_3883393_zqz8ttmm6uk.woff2?t=1689649791403') format('woff2'),
url('//at.alicdn.com/t/c/font_3883393_zqz8ttmm6uk.woff?t=1689649791403') format('woff'),
url('//at.alicdn.com/t/c/font_3883393_zqz8ttmm6uk.ttf?t=1689649791403') format('truetype');
}
.iconfont {
@ -13,6 +13,50 @@
-moz-osx-font-smoothing: grayscale;
}
.iconmofang-liangzuoliangyou:before {
content: "\e6c5";
}
.iconmofang-yishangliangxia:before {
content: "\e6c6";
}
.iconmofang-yizuoliangyou:before {
content: "\e6c7";
}
.iconxuanzemoban-yizuosanyou:before {
content: "\e6e9";
}
.iconrequ:before {
content: "\e68d";
}
.iconmofang1:before {
content: "\e64d";
}
.iconxinyongqia:before {
content: "\e785";
}
.iconmendian:before {
content: "\e60a";
}
.iconico_yuyueguanli_yuyuebiangeng:before {
content: "\e94a";
}
.iconsousuo:before {
content: "\e8b9";
}
.icongengduo:before {
content: "\e63b";
}
.icona-02_luxian:before {
content: "\e687";
}

View File

@ -5,6 +5,83 @@
"css_prefix_text": "icon",
"description": "系统图标",
"glyphs": [
{
"icon_id": "9924690",
"name": "魔方-两左两右",
"font_class": "mofang-liangzuoliangyou",
"unicode": "e6c5",
"unicode_decimal": 59077
},
{
"icon_id": "9924691",
"name": "魔方-一上两下",
"font_class": "mofang-yishangliangxia",
"unicode": "e6c6",
"unicode_decimal": 59078
},
{
"icon_id": "9924700",
"name": "魔方-一左两右",
"font_class": "mofang-yizuoliangyou",
"unicode": "e6c7",
"unicode_decimal": 59079
},
{
"icon_id": "18287302",
"name": "选择模板-一左三右",
"font_class": "xuanzemoban-yizuosanyou",
"unicode": "e6e9",
"unicode_decimal": 59113
},
{
"icon_id": "33490811",
"name": "热区",
"font_class": "requ",
"unicode": "e68d",
"unicode_decimal": 59021
},
{
"icon_id": "30454135",
"name": "魔方",
"font_class": "mofang1",
"unicode": "e64d",
"unicode_decimal": 58957
},
{
"icon_id": "579624",
"name": "信用卡",
"font_class": "xinyongqia",
"unicode": "e785",
"unicode_decimal": 59269
},
{
"icon_id": "2681698",
"name": "门店",
"font_class": "mendian",
"unicode": "e60a",
"unicode_decimal": 58890
},
{
"icon_id": "6607852",
"name": "ico_预约管理_预约变更",
"font_class": "ico_yuyueguanli_yuyuebiangeng",
"unicode": "e94a",
"unicode_decimal": 59722
},
{
"icon_id": "11372706",
"name": "搜索",
"font_class": "sousuo",
"unicode": "e8b9",
"unicode_decimal": 59577
},
{
"icon_id": "1703513",
"name": "更多",
"font_class": "gengduo",
"unicode": "e63b",
"unicode_decimal": 58939
},
{
"icon_id": "36212724",
"name": "02_路线",

View File

@ -49,15 +49,18 @@ class Request {
// 全局响应拦截器
this.instance.interceptors.response.use(
(response: requestResponse) => {
const res = response.data
if (res.code != 1) {
this.handleAuthError(res.code)
if (res.code != 401) ElMessage({ message: res.msg, type: 'error' })
return Promise.reject(new Error(res.msg || 'Error'))
} else {
if (response.config.showSuccessMessage) ElMessage({ message: res.msg, type: 'success' })
return res
}
if (response.request.responseType != 'blob') {
const res = response.data
if (res.code != 1) {
this.handleAuthError(res.code)
if (res.code != 401) ElMessage({ message: res.msg, type: 'error' })
return Promise.reject(new Error(res.msg || 'Error'))
} else {
if (response.config.showSuccessMessage) ElMessage({ message: res.msg, type: 'success' })
return res
}
}
return response.data
},
(err: any) => {
this.handleNetworkError(err)

View File

@ -0,0 +1,167 @@
<template>
<div class="main main-container min-w-[1000px] min-h-[650px]" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<div v-if="authinfo">
<el-row>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('authApp') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.app.app_name }}</span>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('authAppKey') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.app.app_key }}</span>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('companyName') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.app_auth.company_name }}</span>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="mt-[15px]">
<div class="flex">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('siteAddress') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.site_address }}</span>
</div>
<div class="flex" v-if="!authinfo.address_type">
<span class="text-[14px] min-w-[130px] text-right mr-[20px]"></span>
<span class="text-[14px] text-[#f10b0b] cursor-pointer" @click="authEvent">{{ t('siteAddressTips') }}</span>
</div>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('contactName') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.app_auth.contact_name }}</span>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('authCode') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.auth_code }}</span>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('authSecret') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.auth_secret }}</span>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('createTime') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.create_time }}</span>
</div>
</el-col>
<el-col :span="8" class="mb-[20px]">
<div class="flex mt-[15px]">
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('expireTime') }}</span>
<span class="text-[14px] text-[#666666]">{{ authinfo.expire_time }}</span>
</div>
</el-col>
</el-row>
</div>
<div v-else>
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<el-form-item :label="t('authCode')">
<el-input v-model="formData.auth_code" :placeholder="t('authCodePlaceholder')" class="input-width" clearable/>
</el-form-item>
<el-form-item :label="t('authSecret')">
<el-input v-model="formData.auth_secret" clearable :placeholder="t('authSecretPlaceholder')" class="input-width"/>
</el-form-item>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { getAuthinfo, setAuthinfo, getAdminAuthinfo } from '@/api/module'
import { img } from '@/utils/common'
import { FormInstance, FormRules } from 'element-plus'
import { useRouter } from 'vue-router'
const router = useRouter()
const authinfo = ref("")
const loading = ref(true)
const checkAppMange = () => {
getAuthinfo().then(res => {
loading.value = false
if(res.data.data && res.data.data.length != 0){
authinfo.value = res.data.data;
}
}).catch(() => {
loading.value = false
})
}
checkAppMange()
const formData = reactive<Record<string, string>>({
auth_code: '',
auth_secret: ''
})
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({
})
const setFormData = async () => {
const data = await (await getAdminAuthinfo()).data
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
setFormData()
const save = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
setAuthinfo(formData).then(() => {
loading.value = false
checkAppMange()
}).catch(() => {
loading.value = false
})
}
})
}
const authEvent = () => {
authinfo.value = ""
}
</script>
<style lang="scss" scoped>
.app-text {
overflow:hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow:ellipsis;
}
.main {
background-color: var(--el-bg-color-overlay);
}
.app-item {
// box-shadow: 0px 6px 18px 0px rgba(82,129,187,0.1);
}
</style>

View File

@ -6,10 +6,10 @@
<img src="@/assets/images/app_store/local_icon_select.png" class="mr-1.5 w-3.5 h-3.5 mb-0.5">
{{ t('localAppText') }}
</div>
<div class="border rounded-sm switch-btn px-4 py-1 ml-3 cursor-pointer flex items-center" @click="market()">
<!-- <div class="border rounded-sm switch-btn px-4 py-1 ml-3 cursor-pointer flex items-center" @click="market()">
<img src="@/assets/images/app_store/market_icon.png" class="mr-1.5 w-3.5 h-3.5 mb-0.5">
{{ t('marketAppText') }}
</div>
</div> -->
</div>
<div class="relative">
<div class="absolute right-0 top-[2px] flex items-center cursor-pointer z-20 border border-inherit">
@ -20,13 +20,13 @@
<img src="@/assets/images/app_store/switch_icon_2.png" class="w-[16px] h-[16px] ">
</div>
</div>
<el-tabs v-model="activeName" class="demo-tabs " @tab-click="handleClick">
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane :label="installLabel" name="installed">
<div class="flex flex-wrap px-2 plug-list pb-10">
<div v-for="(item, index) in localList.installed" :key="index + 'a'" class="flex items-center cursor-pointer w-[295px] relative plug-item mr-4 mb-4" @click="getAddonDetialFn(item)" v-if="showType == 'small'">
<div class="p-3">
<img class="w-[44px] h-[44px] rounded-sm" v-if="item.icon" :src="img(item.icon)" alt="">
<img class="w-[44px] h-[44px] rounded-sm" v-if="item.icon" :src="item.icon" alt="">
<img class="w-[44px] h-[44px] rounded-sm" v-else src="@/assets/images/icon-addon.png" alt="">
</div>
<div class="flex items-center w-[220px] border-b py-3 justify-between">
@ -41,7 +41,7 @@
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="showType == 'large'">
<div class="app-item cursor-pointer mr-4 mt-[20px] pb-2 bg-[#f7f7f7]" v-for="(item, index) in localList.installed" :key="index + 'a'" @click="getAddonDetialFn(item)">
<div class="flex justify-center items-center">
<img class="w-[240px] h-[120px]" v-if="item.cover" :src="img(item.cover)" />
<img class="w-[240px] h-[120px]" v-if="item.cover" :src="item.cover" />
<img v-else class="w-[240px] h-[120px]" src="@/assets/images/app_store/app_store_default.png" />
</div>
<div class="flex w-[240px] h-[46px]">
@ -65,25 +65,25 @@
<div v-for="(item, index) in localList.uninstalled" :key="index + 'a'" class="flex items-center cursor-pointer w-[295px] relative plug-item mr-4 mb-4" @click="getAddonDetialFn(item)" v-if="showType == 'small'">
<div class="p-3">
<!-- <img class="w-[44px] h-[44px] rounded-sm" v-if="item.icon" :src="img(item.icon)" alt=""> -->
<img class="w-[44px] h-[44px] rounded-sm" src="@/assets/images/icon-addon.png" alt="">
<img v-if="item.icon" class="w-[44px] h-[44px] rounded-sm" :src="img(item.icon)" alt="">
<img v-else class="w-[44px] h-[44px] rounded-sm" src="@/assets/images/icon-addon.png" alt="">
</div>
<div class="flex items-center w-[220px] border-b py-3 justify-between">
<div class="flex flex-col">
<span class="text-[14px] truncate w-[160px]">{{ item.title }}</span>
<span class="text-xs text-gray-400 truncate w-[160px] mt-[4px]">{{ item.desc }}</span>
</div>
<span class="w-max flex items-center plug-item-operate border rounded-2xl px-3.5 py-1.5 leading-none" @click.stop="installAddonFn(item.key)">{{ t('install') }}</span>
<span v-if="item.is_download" class="w-max flex items-center plug-item-operate border rounded-2xl px-3.5 py-1.5 leading-none" @click.stop="installAddonFn(item.key)">{{ t('install') }}</span>
<span v-else class="w-max flex items-center plug-item-operate border rounded-2xl px-3.5 py-1.5 leading-none" @click.stop="downEvent(item.key)">{{ t('down') }}</span>
</div>
</div>
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="showType == 'large'">
<div class="app-item cursor-pointer mr-4 mt-[20px] pb-2 bg-[#f7f7f7]" v-for="(item, index) in localList.uninstalled" :key="index + 'a'" @click="getAddonDetialFn(item)">
<div class="flex justify-center items-center">
<!-- <img class="w-[240px] h-[120px]" v-if="item.cover" :src="img(item.cover)"/> -->
<img class="w-[240px] h-[120px]" src="@/assets/images/app_store/app_store_default.png" />
<img v-if="item.cover && !item.is_download" class="w-[240px] h-[120px]" :src="img(item.cover)"/>
<img v-else-if="item.cover && item.is_download" class="w-[240px] h-[120px]" :src="item.cover"/>
<img v-else class="w-[240px] h-[120px]" src="@/assets/images/app_store/app_store_default.png" />
</div>
<div class="flex w-[240px] h-[46px]">
<div class="text-left mt-2 w-[190px]">
@ -91,9 +91,9 @@
<p class="app-text text-[12px] text-[#999] pl-2">{{ item.desc }}</p>
</div>
<div class="flex items-center pr-2">
<span class="w-max flex items-center plug-item-operate border rounded-2xl px-2 py-1 leading-none mt-[10px]" @click.stop="installAddonFn(item.key)">{{ t('install') }}</span>
<span v-if="item.is_download" class="w-max flex items-center plug-item-operate border rounded-2xl px-2 py-1 leading-none mt-[10px]" @click.stop="installAddonFn(item.key)">{{ t('install') }}</span>
<span v-else class="w-max flex items-center plug-item-operate border rounded-2xl px-2 py-1 leading-none mt-[10px]" @click.stop="downEvent(item.key)">{{ t('down') }}</span>
</div>
</div>
</div>
</div>
@ -101,6 +101,49 @@
<el-empty :description="t('noPlug')" v-if="!localList.uninstalled.length && !loading" class="mx-auto" />
</div>
</el-tab-pane>
<el-tab-pane :label="allLabel" name="buy">
<div class="flex flex-wrap px-2 plug-list pb-10">
<div v-for="(item, index) in localList.all" :key="index + 'a'" class="flex items-center cursor-pointer w-[295px] relative plug-item mr-4 mb-4" @click="getAddonDetialFn(item)" v-if="showType == 'small'">
<div class="p-3">
<img v-if="item.icon" class="w-[44px] h-[44px] rounded-sm" :src="img(item.icon)" alt="">
<img v-else class="w-[44px] h-[44px] rounded-sm" src="@/assets/images/icon-addon.png" alt="">
</div>
<div class="flex items-center w-[220px] border-b py-3 justify-between">
<div class="flex flex-col">
<span class="text-[14px] truncate w-[160px]">{{ item.title }}</span>
<span class="text-xs text-gray-400 truncate w-[160px] mt-[4px]">{{ item.desc }}</span>
</div>
<span v-if="item.install_info && Object.keys(item.install_info)?.length" class="w-max flex items-center plug-item-operate border rounded-2xl px-3.5 py-1.5 leading-none " @click.stop="uninstallAddonFn(item.key)">{{ t('unload') }}</span>
<span v-else-if="item.is_download && item.install_info <= 0" class="w-max flex items-center plug-item-operate border rounded-2xl px-3.5 py-1.5 leading-none" @click.stop="installAddonFn(item.key)">{{ t('install') }}</span>
<span v-else class="w-max flex items-center plug-item-operate border rounded-2xl px-3.5 py-1.5 leading-none" @click.stop="downEvent(item.key)">{{ t('down') }}</span>
</div>
</div>
<div class="flex flex-wrap plug-list pb-10 plug-large" v-if="showType == 'large'">
<div class="app-item cursor-pointer mr-4 mt-[20px] pb-2 bg-[#f7f7f7]" v-for="(item, index) in localList.all" :key="index + 'a'" @click="getAddonDetialFn(item)">
<div class="flex justify-center items-center">
<img v-if="item.cover && !item.is_download" class="w-[240px] h-[120px]" :src="img(item.cover)"/>
<img v-else-if="item.cover && item.is_download" class="w-[240px] h-[120px]" :src="item.cover"/>
<img v-else class="w-[240px] h-[120px]" src="@/assets/images/app_store/app_store_default.png" />
</div>
<div class="flex w-[240px] h-[46px]">
<div class="text-left mt-2 w-[190px]">
<p class="app-text text-[14px] text-[#222] pl-2">{{ item.title }}</p>
<p class="app-text text-[12px] text-[#999] pl-2">{{ item.desc }}</p>
</div>
<div class="flex items-center pr-2">
<span v-if="item.install_info && Object.keys(item.install_info)?.length" class="w-max flex items-center plug-item-operate border rounded-2xl px-3.5 py-1.5 leading-none " @click.stop="uninstallAddonFn(item.key)">{{ t('unload') }}</span>
<span v-else-if="item.is_download && item.install_info <= 0" class="w-max flex items-center plug-item-operate border rounded-2xl px-2 py-1 leading-none mt-[10px]" @click.stop="installAddonFn(item.key)">{{ t('install') }}</span>
<span v-else class="w-max flex items-center plug-item-operate border rounded-2xl px-2 py-1 leading-none mt-[10px]" @click.stop="downEvent(item.key)">{{ t('down') }}</span>
</div>
</div>
</div>
</div>
<el-empty :description="t('noPlug')" v-if="!localList.all.length && !loading" class="mx-auto" />
</div>
</el-tab-pane>
</el-tabs>
</div>
@ -279,21 +322,27 @@
</template>
<script lang="ts" setup>
import { ref, watch, computed } from 'vue'
import { ref, watch, computed, reactive } from 'vue'
import { t } from '@/lang'
import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, getAddonInstallTaskState, executeInstall } from '@/api/addon'
import { downloadVersion } from '@/api/module'
import { TabsPaneContext, ElMessageBox } from 'element-plus'
import { img } from '@/utils/common'
import { Terminal, api as terminalApi } from 'vue-web-terminal'
import { useRouter, useRoute } from 'vue-router'
const route = useRoute()
const router = useRouter()
const activeName = ref('installed')
const loading = ref<Boolean>(false)
const handleClick = (tab: TabsPaneContext, event: Event) => {
}
const showType = ref('large')
const downEvent = (key: string) => {
downloadVersion(key).then(()=>{
localListFn()
})
}
const installLabel = computed(() => {
let text = t('installLabel')
localList.value.installed.length && (text += ` (${localList.value.installed.length})`)
@ -306,35 +355,58 @@ const uninstalledLabel = computed(() => {
return text
})
const allLabel = computed(() => {
let text = t('buyLabel')
localList.value.all.length && (text += ` (${localList.value.all.length})`)
return text
})
/**
* 本地下载的插件列表
*/
const localList = ref({
installed: [],
uninstalled: []
uninstalled: [],
all: [],
error: ''
})
const localListFn = () => {
loading.value = true
getAddonLocal({}).then(res => {
const data = res.data
const data = res.data.list
localList.value.error = res.data.error
localList.value.installed = []
localList.value.uninstalled = []
for(let i in data){
if(data[i].is_local == false) localList.value.all.push(data[i])
if (data[i].install_info && Object.keys(data[i].install_info)?.length) {
localList.value.installed.push(data[i])
} else {
if(data[i].is_download == true) localList.value.uninstalled.push(data[i])
}
}
data.forEach(element => {
if (element.install_info && Object.keys(element.install_info)?.length) {
localList.value.installed.push(element)
} else {
localList.value.uninstalled.push(element)
}
})
loading.value = false
}).catch(() => {
loading.value = false
})
}
localListFn()
const handleClick = (tab: TabsPaneContext, event: Event) => {
if(tab.paneName == 'buy' && localList.value.error != ''){
ElMessage({
message: localList.value.error,
grouping: true,
type: 'error'
})
}
}
const currAddon = ref('')
//
const installShowDialog = ref(false)

View File

@ -34,7 +34,6 @@
</template>
</el-table-column>
<el-table-column prop="sort" :label="t('sort')" min-width="100" />
<el-table-column prop="create_time" :label="t('createTime')" width="180" />
<el-table-column :label="t('operation')" fixed="right" width="130">
<template #default="{ row }">
<!-- <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>

View File

@ -35,7 +35,6 @@
</template>
</el-table-column>
<el-table-column prop="sort" :label="t('sort')" min-width="100" />
<el-table-column prop="create_time" :label="t('createTime')" width="180" />
<el-table-column :label="t('operation')" fixed="right" width="130">
<template #default="{ row }">
<!-- <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>

View File

@ -90,13 +90,13 @@
</el-col>
</el-row>
</el-card>
<el-dialog v-model="showDialog" :title="t('versionCode')" width="550px" :destroy-on-close="true">
<el-dialog v-model="showDialog" :title="t('versionCode')" width="650px" :destroy-on-close="true">
<el-table :data="weappTableData.data" size="large" v-loading="weappTableData.loading">
<el-table-column prop="version" :label="t('version')" min-width="100" />
<el-table-column prop="create_time" :label="t('createTime')" min-width="150" />
<el-table-column :label="t('operation')" fixed="right" width="100">
<template #default="{ row }">
<el-button type="danger" link @click="down(row.id)">{{ t('down') }}</el-button>
<el-button type="danger" link @click="down(row)" >{{ t('down') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -119,6 +119,7 @@ import { getWeappConfig, getVersionList, versionDown } from '@/api/weapp'
import { t } from '@/lang'
import { useClipboard } from '@vueuse/core'
import { ElMessage, FormInstance, FormRules } from 'element-plus'
import axios, { HttpStatusCode } from 'axios'
import { useRoute } from 'vue-router'
const route = useRoute()
@ -145,7 +146,7 @@ const checkWeappConfig = () =>{
}else{
ElMessage(t('weappTips'))
setTimeout(function() {
history.back()
history.back('/channel/weapp/config')
}, 500);
}
})
@ -162,6 +163,7 @@ const loadWeappTemplateList = (page: number = 1) => {
getVersionList({
page: weappTableData.page,
limit: weappTableData.limit,
type: 'weapp'
}).then(res => {
weappTableData.loading = false
weappTableData.data = res.data.data
@ -172,8 +174,16 @@ const loadWeappTemplateList = (page: number = 1) => {
}
loadWeappTemplateList()
const down = (id) => {
versionDown(id).then(()=>{})
const down = (item) => {
versionDown(item.id).then(res=>{
let blob = new Blob([res]);
let url = window.URL.createObjectURL(blob); // url blob
let a = document.createElement('a');
a.href = url;
a.download = t('weappVersion') + item.version + '.zip';
a.click();
window.URL.revokeObjectURL(url); // url
})
}
</script>

View File

@ -0,0 +1,73 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('imageSet') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<div ref="imageBoxRef">
<div class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('image')">
<upload-image v-model="diyStore.editComponent.imageUrl" :limit="1" @change="selectImg"/>
</el-form-item>
<el-form-item :label="t('hotAreaSet')">
<heat-map v-model="diyStore.editComponent"/>
</el-form-item>
</div>
</div>
</el-form>
</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'
const diyStore = useDiyStore()
diyStore.editComponent.ignore = []; //
//
diyStore.editComponent.verify = (index: number) => {
var res = {code: true, message: ''};
if (diyStore.value[index].imageUrl === '') {
res.code = false;
res.message = t('imageUrlTip');
return res;
}
return res;
};
const selectImg = (url: string) => {
handleHeight();
};
//
const handleHeight = () => {
let image = new Image();
image.src = img(diyStore.editComponent.imageUrl);
image.onload = async () => {
diyStore.editComponent.imgWidth = image.width;
diyStore.editComponent.imgHeight = image.height;
};
}
defineExpose({})
</script>
<style lang="scss" scoped>
</style>

View File

@ -117,7 +117,7 @@
var ratio = item.imgHeight / item.imgWidth;
item.width = 375;
item.height = item.width * ratio;
diyStore.editComponent.imageHeight = item.height;
diyStore.editComponent.imageHeight = parseInt(item.height);
}
};
});

View File

@ -0,0 +1,639 @@
<template>
<!-- 内容 -->
<div class="content-wrap rubik-cube" 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('template')">
<span>{{ selectTemplate.name }}</span>
</el-form-item>
<ul class="selected-template-list">
<li v-for="(item,i) in templateList" :class="[(item.className == diyStore.editComponent.mode) ? 'selected' : '' ]" @click="changeTemplateList(i)">
<icon :name="'iconfont-' + item.src" size="16px"/>
</li>
</ul>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('魔方布局') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<ul class="layout">
<li v-for="(li,i) in selectTemplate.dimensionScale" :class="[selectTemplate.className]">
<div class="have-preview-image" v-show="diyStore.editComponent.list[i].imageUrl">
<img :src="img(diyStore.editComponent.list[i].imageUrl)"/>
</div>
<div class="empty" :class="[selectTemplate.className]" v-show="!diyStore.editComponent.list[i].imageUrl">
<p>{{li.name}}</p>
<p>{{li.desc}}</p>
</div>
</li>
</ul>
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('image')">
<upload-image v-model="item.imageUrl" :limit="1" @change="selectImg"/>
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="item.link"/>
</el-form-item>
</div>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('rubikCubeStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('imageGap')">
<el-slider v-model="diyStore.editComponent.imageGap" show-input size="small" class="ml-[10px] horz-blank-slider" :max="30"/>
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] horz-blank-slider" :max="50"/>
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] horz-blank-slider" :max="50"/>
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
import {ref, watch, onMounted, nextTick, computed} from 'vue'
import {t} from '@/lang'
import useDiyStore from '@/stores/modules/diy'
import {img} from '@/utils/common'
const diyStore = useDiyStore()
diyStore.editComponent.ignore = []; //
//
diyStore.editComponent.verify = (index: number) => {
var res = {code: true, message: ''};
diyStore.value[index].list.forEach((item: any) => {
if (item.imageUrl === '') {
res.code = false;
res.message = t('imageUrlTip');
return res;
}
});
return res;
};
const templateList = ref([
{
name: "1行2个",
src: 'iconyihangliangge',
className: "row1-of2",
dimensionScale: [
{
desc: "宽度50%",
size: "200px * 200px",
name: "图一"
},
{
desc: "宽度50%",
size: "200px * 200px",
name: "图二"
}
],
descAux: "选定布局区域在下方添加图片建议添加尺寸一致的图片宽度最小建议为200px"
},
{
name: "1行3个",
src: 'iconyihangsange',
className: "row1-of3",
dimensionScale: [
{
desc: "宽度33.33%",
size: "200px * 200px",
name: "图一"
},
{
desc: "宽度33.33%",
size: "200px * 200px",
name: "图二"
},
{
desc: "宽度33.33%",
size: "200px * 200px",
name: "图三"
}
],
descAux: "选定布局区域在下方添加图片建议添加尺寸一致的图片宽度最小建议为130px"
},
{
name: "1行4个",
src: 'iconyihangsige',
className: "row1-of4",
dimensionScale: [
{
desc: "宽度25%",
size: "200px * 200px",
name: "图一"
},
{
desc: "宽度25%",
size: "200px * 200px",
name: "图二"
},
{
desc: "宽度25%",
size: "200px * 200px",
name: "图三"
},
{
desc: "宽度25%",
size: "200px * 200px",
name: "图四"
}
],
descAux: "选定布局区域在下方添加图片建议添加尺寸一致的图片宽度最小建议为100px"
},
{
name: "2左2右",
src: 'iconmofang-liangzuoliangyou',
className: "row2-lt-of2-rt",
dimensionScale: [
{
desc: "宽度50%",
size: "200px * 200px",
name: "图一"
},
{
desc: "宽度50%",
size: "200px * 200px",
name: "图二"
},
{
desc: "宽度50%",
size: "200px * 200px",
name: "图三"
},
{
desc: "宽度50%",
size: "200px * 200px",
name: "图四"
}
],
descAux: "选定布局区域在下方添加图片建议添加尺寸一致的图片宽度最小建议为200px"
},
{
name: "1左2右",
src: 'iconmofang-yizuoliangyou',
className: "row1-lt-of2-rt",
dimensionScale: [
{
desc: "宽度50% * 高度100%",
size: "200px * 400px",
name: "图一"
},
{
desc: "宽度50% * 高度50%",
size: "200px * 200px",
name: "图二"
},
{
desc: "宽度50% * 高度50%",
size: "200px * 200px",
name: "图三"
}
],
descAux: "选定布局区域在下方添加图片宽度最小建议为200px右侧两张图片高度一致左侧图片高度为右侧两张图片高度之和左侧图片尺寸200px * 300px右侧两张图片尺寸200px * 150px"
},
{
name: "1上2下",
src: 'iconmofang-yishangliangxia',
className: "row1-tp-of2-bm",
dimensionScale: [
{
desc: "宽度100% * 高度50%",
size: "400px * 200px",
name: "图一"
},
{
desc: "宽度50% * 高度50%",
size: "200px * 200px",
name: "图二"
},
{
desc: "宽度50% * 高度50%",
size: "200px * 200px",
name: "图三"
}
],
descAux: "选定布局区域在下方添加图片上方一张图片的宽度为下方两张图片宽度之和下放两张图片尺寸一致高度可根据实际需求自行确定上方图片尺寸400px * 150px下方两张图片尺寸200px * 150px"
},
{
name: "1左3右",
src: 'iconxuanzemoban-yizuosanyou',
className: "row1-lt-of1-tp-of2-bm",
dimensionScale: [
{
desc: "宽度50% * 高度100%",
size: "200px * 400px",
name: "图一"
},
{
desc: "宽度50% * 高度50%",
size: "200px * 200px",
name: "图二"
},
{
desc: "宽度25% * 高度50%",
size: "100px * 200px",
name: "图三"
},
{
desc: "宽度25% * 高度50%",
size: "100px * 200px",
name: "图四"
}
],
descAux: "选定布局区域在下方添加图片左右两侧内容宽高相同右侧上下区域高度各占50%右侧内容下半部分两张图片的宽度相同各占右侧内容宽度的50%左侧图片尺寸200px * 400px右侧上半部分图片尺寸200px * 200px右侧下半部分两张图片尺寸100px * 200px"
}
]);
const selectTemplate = computed(() => {
var data;
templateList.value.forEach((item) => {
if (item.className == diyStore.editComponent.mode) {
data = item;
}
})
return data;
});
const changeTemplateList = (v: number) => {
for (var i = 0; i < templateList.value.length; i++) {
if (i == v) {
diyStore.editComponent.mode = templateList.value[i].className;
var count = templateList.value[i].dimensionScale.length;
//
//
if (count > diyStore.editComponent.list.length) {
for (var j = 0; j < count; j++) {
if ((j + 1) > diyStore.editComponent.list.length) diyStore.editComponent.list.push({
imageUrl: "",
imgWidth: 0,
imgHeight: 0,
link: {name: ""}
});
}
} else {
//
if (count != diyStore.editComponent.list.length) {
for (var j = 0; j < diyStore.editComponent.list.length; j++) {
if ((j + 1) > count) {
diyStore.editComponent.list.splice(j, 1);
j = 0;
}
}
}
}
}
}
};
const selectImg = (url:string)=> {
handleHeight(true);
};
//
const handleHeight = (isCalcHeight:boolean = false)=> {
diyStore.editComponent.list.forEach((item: any, index: number) => {
let image = new Image();
image.src = img(item.imageUrl);
image.onload = async () => {
item.imgWidth = image.width;
item.imgHeight = image.height;
console.log('handleHeight',item)
};
});
}
defineExpose({})
</script>
<style lang="scss" scoped>
.rubik-cube .selected-template-list {
/*padding-left: 15px;*/
margin-bottom: 20px;
overflow: hidden;
display: flex;
flex-wrap: wrap;
li {
color: #909399;
width: 46px;
height: 32px;
text-align: center;
line-height: 29px;
border: 1px solid #e5e5e5;
cursor: pointer;
background: #ffffff;
box-sizing: border-box;
border-right: 1px transparent solid;
&:last-child {
border-right: 1px solid #e5e5e5;
}
&.selected {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
}
img {
display: inline-block;
}
div {
font-size: 12px;
}
}
}
.layout {
overflow: hidden;
position: relative;
margin-bottom: 15px;
li {
float: left;
color: #909399;
border: 1px solid #e5e5e5;
cursor: pointer;
font-size: 12px;
position: relative;
div.empty {
left: 0;
text-align: center;
width: 100%;
position: absolute;
top: 50%;
margin-top: -26px;
p {
margin: 0;
line-height: 26px;
}
}
div.have-preview-image {
box-sizing: border-box;
img {
display: inline-block;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
}
}
// 12
&.row1-of2 {
width: 49.2%;
height: 160px;
border-right: 1px transparent solid;
&:last-child {
border-right: 1px solid #e5e5e5;
}
div.empty {
}
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
// 13
&.row1-of3 {
width: 32.5%;
height: 100px;
border-right: 1px transparent solid;
&:last-child {
border-right: 1px solid #bdf;
}
div.empty {
}
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 100px;
background: #ffffff;
}
}
// 14
&.row1-of4 {
width: 24.2%;
height: 80px;
border-right: 1px transparent solid;
&:last-child {
border-right: 1px solid #bdf;
}
div.empty {
}
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 80px;
background: #ffffff;
}
}
// 22
&.row2-lt-of2-rt {
width: 49.2%;
height: 160px;
&:nth-child(1) {
border-right: 1px transparent solid;
border-bottom: 1px transparent solid;
}
&:nth-child(2) {
border-bottom: 1px transparent solid;
}
&:nth-child(3) {
border-right: 1px transparent solid;
clear: both;
}
div.empty {
}
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
// 12
&.row1-lt-of2-rt {
width: 49.2%;
font-size: 12px;
&:nth-child(1) {
height: 322px;
border-right: 1px transparent solid;
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 322px;
background: #ffffff;
}
}
&:nth-child(2) {
height: 160px;
border-bottom: 1px transparent solid;
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
&:nth-child(3) {
height: 160px;
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
div.empty {
}
}
// 12
&.row1-tp-of2-bm {
height: 160px;
&:nth-child(1) {
width: 99.4%;
border-bottom: 1px transparent solid;
}
&:nth-child(2) {
width: 49.2%;
border-right: 1px transparent solid;
}
&:nth-child(3) {
width: 49.2%;
}
div.empty {
}
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
// 13
&.row1-lt-of1-tp-of2-bm {
&:nth-child(1) {
height: 320px;
width: 49.2%;
border-right: 1px transparent solid;
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 320px;
background: #ffffff;
}
}
&:nth-child(2) {
height: 160px;
width: 49.2%;
border-bottom: 1px transparent solid;
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
&:nth-child(3) {
height: 160px;
width: 24.2%;
border-right: 1px transparent solid;
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
&:nth-child(4) {
height: 160px;
width: 24.2%;
div.have-preview-image {
text-align: center;
height: 100%;
line-height: 160px;
background: #ffffff;
}
}
div.empty {
}
}
}
}
</style>

View File

@ -284,6 +284,7 @@ initPage({
diyStore.typeName = data.type_name;
diyStore.templateName = data.template;
diyStore.isDefault = data.is_default;
diyStore.pageMode = data.mode;
if (data.value) {
let sources = JSON.parse(data.value);
diyStore.global = sources.global;

View File

@ -0,0 +1,187 @@
<template>
<el-card class="box-card !border-none" shadow="never" v-loading="payLoading">
<!-- 设置支付配置 -->
<div class="flex justify-between items-center">
<span class="text-[20px]">{{ pageName }}</span>
</div>
<div class="mt-[10px]">
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="payListTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('outTradeNo')" prop="trade_no">
<el-input v-model="payListTable.searchParam.out_trade_no"
:placeholder="t('outTradeNoPlaceholder')" />
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker v-model="payListTable.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 :label="t('status')" prop="status">
<el-select v-model="payListTable.searchParam.status" placeholder="Select">
<el-option :label="t('all')" value="" />
<el-option :label="t('waitAudit')" value="3" />
<el-option :label="t('passed')" value="2" />
<el-option :label="t('notPass')" value="-1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadPayList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<el-table :data="payListTable.data" size="large" v-loading="payListTable.loading">
<template #empty>
<span>{{ !payListTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="out_trade_no" :label="t('outTradeNo')" min-width="230"
:show-overflow-tooltip="true" />
<el-table-column prop="body" :label="t('body')" min-width="150" :show-overflow-tooltip="true" />
<el-table-column prop="money" :label="t('money')" min-width="120" align="right" />
<el-table-column :label="t('createTime')" min-width="150" align="center" :show-overflow-tooltip="true">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column>
<el-table-column :label="t('status')" min-width="150" align="center">
<template #default="{ row }">
<span v-if="row.status == 3">{{ t('waitAudit') }}</span>
<span v-if="row.status == 2" class="text-success">{{ t('passed') }}</span>
<span v-if="row.status == -1" class="text-error">{{ t('notPass') }}</span>
</template>
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" width="240" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="detailEvent(row)">{{ t('detail') }}</el-button>
<span v-if="row.status == 3">
<el-button type="success" link @click="passEvent(row)">{{ t('pass') }}</el-button>
<el-button type="danger" link @click="refuseEvent(row)">{{ t('refuse') }}</el-button>
</span>
<el-button type="primary" link @click="voucherEvent(row)">{{ t('voucher') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination v-model:current-page="payListTable.page" v-model:page-size="payListTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="payListTable.total"
@size-change="loadPayList()" @current-change="loadPayList" />
</div>
</div>
</el-card>
<el-image-viewer :url-list="previewImageList" v-if="imageViewerShow" @close="imageViewerShow = false" :initial-index="0"
:zoom-rate="1" />
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { getPayAuditList, payAuditPass, payAuditRefuse } from '@/api/sys'
import { AnyObject } from '@/types/global'
import { ElMessageBox, FormInstance } from 'element-plus'
import { img } from '@/utils/common'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const previewImageList = ref<string[]>([])
const imageViewerShow = ref(false)
const payListTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
out_trade_no: '',
create_time: '',
status: ''
}
})
const searchFormRef = ref<FormInstance>()
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadPayList()
}
/**
* 获取站点账单记录列表
*/
const loadPayList = (page: number = 1) => {
payListTable.loading = true
payListTable.page = page
getPayAuditList({
page: payListTable.page,
limit: payListTable.limit,
...payListTable.searchParam
}).then(res => {
payListTable.loading = false
payListTable.data = res.data.data
payListTable.total = res.data.total
}).catch(() => {
payListTable.loading = false
})
}
loadPayList()
const passEvent = (row: AnyObject) => {
ElMessageBox.confirm(
t('passTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(({ value }) => {
payAuditPass(row.out_trade_no)
.then(() => {
loadPayList()
})
.catch()
}).catch(() => {
})
}
const refuseEvent = (row: AnyObject) => {
ElMessageBox.prompt(t('refuseReason'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
inputErrorMessage: t('refuseReason'),
inputPattern: /\S/,
inputType: 'textarea'
}).then(({ value }) => {
payAuditRefuse({ out_trade_no: row.out_trade_no, reason: value })
.then(() => {
loadPayList()
})
.catch()
}).catch(() => {
})
}
const voucherEvent = (row: AnyObject) => {
previewImageList.value[0] = img(row.voucher)
imageViewerShow.value = true
}
const detailEvent = (row: AnyObject) => {
router.push(`/finance/pay/detail?id=${row.id}`)
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,135 @@
<template>
<div class="main-container">
<div class="detail-head">
<div class="left" @click="router.push({ path: '/finance/pay/offlinepay' })">
<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" v-loading="loading">
<el-form :model="formData" label-width="150px" ref="formRef" class="page-form mt-[10px]" v-if="!loading">
<el-form-item :label="t('outTradeNo')">
<div class="input-width">{{ formData.out_trade_no }}</div>
</el-form-item>
<el-form-item :label="t('createTime')">
<div class="input-width">{{ formData.create_time }}</div>
</el-form-item>
<el-form-item :label="t('money')">
<div class="input-width">{{ formData.money }}</div>
</el-form-item>
<el-form-item :label="t('body')">
<div class="input-width">{{ formData.body }}</div>
</el-form-item>
<el-form-item :label="t('channel')">
<div class="input-width">{{ formData.channel_name }}</div>
</el-form-item>
<el-form-item :label="t('payStatus')">
<div class="input-width">{{ formData.status_name }}</div>
</el-form-item>
<el-form-item :label="t('payType')" v-if="formData.type_name">
<div class="input-width">{{ formData.type_name }}</div>
</el-form-item>
<el-form-item :label="t('payTime')" v-if="formData.pay_time">
<div class="input-width">{{ formData.pay_time }}</div>
</el-form-item>
<el-form-item :label="t('failTime')" v-if="formData.close_time">
<div class="input-width">{{ formData.close_time }}</div>
</el-form-item>
<el-form-item :label="t('failReason')" v-if="formData.fail_reason">
<div class="input-width">{{ formData.fail_reason }}</div>
</el-form-item>
<el-form-item :label="t('voucher')" v-if="formData.type == 'offlinepay' && formData.voucher">
<div class="input-width cursor-pointer">
<el-image style="width: 100%;" :src="img(formData.voucher)" fit="cover" @click="voucherEvent" />
</div>
</el-form-item>
<el-form-item :label="t('auditVoucher')" v-if="formData.type == 'offlinepay' && formData.status == 3">
<el-button type="primary" @click="passEvent">{{ t('pass') }}</el-button>
<el-button type="danger" @click="refuseEvent">{{ t('refuse') }}</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
<el-image-viewer :url-list="previewImageList" v-if="imageViewerShow" @close="imageViewerShow = false" :initial-index="0"
:zoom-rate="1" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { getPayDetail, payAuditPass, payAuditRefuse } from '@/api/sys'
import { img } from '@/utils/common'
import { ElMessageBox } from 'element-plus'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const id: number = parseInt((route.query.id || 0))
const loading = ref(true)
const formData: Record<string, any> | null = ref(null)
const setFormData = async () => {
loading.value = true
formData.value = null
await getPayDetail(id)
.then(({ data }) => {
formData.value = data
})
.catch(() => {
})
loading.value = false
}
setFormData()
const passEvent = () => {
ElMessageBox.confirm(
t('passTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(({ value }) => {
payAuditPass(formData.value.out_trade_no)
.then(() => {
setFormData()
})
.catch()
}).catch(() => {
})
}
const refuseEvent = () => {
ElMessageBox.prompt(t('refuseReason'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
inputErrorMessage: t('refuseReason'),
inputPattern: /\S/,
inputType: 'textarea'
}).then(({ value }) => {
payAuditRefuse({ out_trade_no: formData.value.out_trade_no, reason: value })
.then(() => {
setFormData()
})
.catch()
}).catch(() => {
})
}
const previewImageList = ref<string[]>([])
const imageViewerShow = ref(false)
const voucherEvent = () => {
previewImageList.value[0] = img(formData.value.voucher)
imageViewerShow.value = true
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,19 +1,33 @@
<template>
<el-dialog v-model="showDialog" :title="t('updateAlipay')" 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-item :label="t('status')">
<el-radio-group v-model="formData.status" >
<el-radio label="1">{{ t('startUsing') }}</el-radio>
<el-radio label="0">{{ t('statusDeactivate') }}</el-radio>
</el-radio-group>
<el-dialog v-model="showDialog" :title="t('updateOfflinepay')" 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-item :label="t('collectionName')" prop="config.collection_name">
<el-input v-model="formData.config.collection_name" :placeholder="t('collectionNamePlaceholder')"
class="input-width" show-word-limit clearable />
</el-form-item>
<el-form-item :label="t('collectionBank')" prop="config.collection_bank">
<el-input v-model="formData.config.collection_bank" :placeholder="t('collectionBankPlaceholder')"
class="input-width" clearable />
</el-form-item>
<el-form-item :label="t('collectionAccount')" prop="config.collection_account">
<el-input v-model="formData.config.collection_account" :placeholder="t('collectionAccountPlaceholder')"
class="input-width" clearable />
</el-form-item>
<el-form-item :label="t('collectionDesc')" prop="config.collection_desc">
<el-input v-model="formData.config.collection_desc" :placeholder="t('collectionDescPlaceholder')"
class="input-width" type="textarea" rows="4" clearable />
</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="loading" @click="confirm(formRef)">{{t('confirm')}}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -23,17 +37,24 @@
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { getPayConfig, setPayConfig } from '@/api/sys'
let showDialog = ref(false)
const showDialog = ref(false)
const loading = ref(true)
/**
* 表单数据
*/
const initialFormData = {
const initialFormData = {
type: 'offlinepay',
status: '',
config: {
collection_name: '',
collection_bank: '',
collection_account: '',
collection_desc: ''
},
channel: '',
status: 0,
is_default: 0
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -42,7 +63,18 @@ const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
'config.collection_name': [
{ required: true, message: t('collectionNamePlaceholder'), trigger: 'blur' }
],
'config.collection_bank': [
{ required: true, message: t('collectionBankPlaceholder'), trigger: 'blur' }
],
'config.collection_account': [
{ required: true, message: t('collectionAccountPlaceholder'), trigger: 'blur' }
],
'config.collection_desc': [
{ required: true, message: t('collectionDescPlaceholder'), trigger: 'blur' }
]
}
})
@ -54,36 +86,25 @@ const emit = defineEmits(['complete'])
*/
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
setPayConfig(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
}).catch(err => {
loading.value = false
// showDialog.value = false
})
emit('complete', formData)
showDialog.value = false
}
})
}
const setFormData = async (row: any = null) => {
loading.value = true;
const setFormData = async (data: any = null) => {
loading.value = true
Object.assign(formData, initialFormData)
if (row) {
const data = await (await getPayConfig(row.key)).data
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
formData.channel = data.redio_key.split('_')[0]
formData.status = Number(formData.status)
}
loading.value = false;
loading.value = false
}
defineExpose({
@ -92,4 +113,4 @@ defineExpose({
})
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped></style>

View File

@ -1,14 +1,17 @@
<template>
<el-dialog v-model="showDialog" :title="t('updateWechat')" width="500px" :destroy-on-close="true">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form"
v-loading="loading">
<el-form-item :label="t('mchId')" prop="config.mch_id">
<el-input v-model="formData.config.mch_id" :placeholder="t('mchIdPlaceholder')" class="input-width" maxlength="32" show-word-limit clearable />
<el-input v-model="formData.config.mch_id" :placeholder="t('mchIdPlaceholder')" class="input-width"
maxlength="32" show-word-limit clearable />
<div class="form-tip">{{ t('mchIdTips') }}</div>
</el-form-item>
<el-form-item :label="t('mchSecretKey')" prop="config.mch_secret_key">
<el-input v-model="formData.config.mch_secret_key" :placeholder="t('mchSecretKeyPlaceholder')" class="input-width" maxlength="32" show-word-limit clearable />
<el-input v-model="formData.config.mch_secret_key" :placeholder="t('mchSecretKeyPlaceholder')"
class="input-width" maxlength="32" show-word-limit clearable />
<div class="form-tip">{{ t('mchSecretKeyTips') }}</div>
</el-form-item>
@ -31,7 +34,7 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{t('confirm')}}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -51,7 +54,7 @@ const loading = ref(true)
*/
const initialFormData = {
type: 'wechatpay',
config:{
config: {
mch_id: '',
mch_secret_key: '',
mch_secret_cert: '',
@ -92,9 +95,9 @@ const emit = defineEmits(['complete'])
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if(valid){
emit('complete',formData);
showDialog.value = false;
if (valid) {
emit('complete', formData)
showDialog.value = false
}
})
}
@ -106,8 +109,8 @@ const setFormData = async (data: any = null) => {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
formData['channel'] = data['redio_key'].split('_')[0];
formData['status'] = Number(formData['status']);
formData.channel = data.redio_key.split('_')[0]
formData.status = Number(formData.status)
}
loading.value = false
}

View File

@ -1,144 +1,151 @@
<template>
<el-card class="box-card !border-none" shadow="never" v-loading="payLoading">
<!-- 设置支付配置 -->
<div class="flex justify-between items-center" v-if="!payLoading">
<span class="text-[20px]">{{pageName}}</span>
<el-button type="primary" @click="isEdit = true" ref="setConfigBtn">{{ t('setConfig') }}</el-button>
</div>
<el-card class="box-card box-pay-card !border-none mt-[20px]" shadow="never" v-for="(payItems, payKey) in payConfigData" :key="payKey">
<div class="flex mb-3">
<span class="text-base">{{payItems.name }}</span>
</div>
<div class="pay-table">
<div class="flex items-center pay-table-head table-bg table-item-pd table-item-border justify-between table-bg">
<span class="text-base text-[#999] w-[150px]">{{t('payType')}}</span>
<!-- <span class="text-base font-bold text-[#999] w-[110px]">{{t('settingDefaultPay')}}</span> -->
<span class="text-base text-[#999] w-[110px] text-center">{{t('onState')}}</span>
<span class="text-base text-[#999] w-[80px] text-center" v-if="isEdit">{{t('templateName')}}</span>
</div>
<div ref="fieldBoxRefs" :data-key="payKey">
<div v-for="(childrenItem,childrenIndex) in payItems.pay_type" :key="childrenItem.redio_key" class="flex table-item-border table-item-pd justify-between" :id="payKey+'_'+childrenIndex">
<div class="table-item-flex w-[150px]">
<span v-if="isEdit" class="iconfont icontuodong mr-2 handle cursor-pointer"></span>
<div class="flex items-center select-none">
<div class="mr-[15px] w-[30px] h-[30px]"><img class="w-[30px]" :src="img(childrenItem.icon)" /></div>
<span class="text-base text-[#666]">{{ childrenItem.name }}</span>
</div>
</div>
<!-- <div class="table-item-flex w-[110px] select-none">
<el-radio v-model="payItems.default_pay_type" :label="childrenItem.redio_key" size="large" @change="settingDefault(childrenItem,payKey)">{{ t('settingDefault') }}</el-radio>
<el-card class="box-card !border-none" shadow="never" v-loading="payLoading">
<!-- 设置支付配置 -->
<div class="flex justify-between items-center" v-if="!payLoading">
<span class="text-[20px]">{{ pageName }}</span>
<el-button type="primary" @click="isEdit = true" ref="setConfigBtn">{{ t('setConfig') }}</el-button>
</div>
<el-card class="box-card box-pay-card !border-none mt-[20px]" shadow="never"
v-for="(payItems, payKey) in payConfigData" :key="payKey">
<div class="flex mb-3">
<span class="text-base">{{ payItems.name }}</span>
</div>
<div class="pay-table">
<div
class="flex items-center pay-table-head table-bg table-item-pd table-item-border justify-between table-bg">
<span class="text-base text-[#999] w-[150px]">{{ t('payType') }}</span>
<!-- <span class="text-base font-bold text-[#999] w-[110px]">{{t('settingDefaultPay')}}</span> -->
<span class="text-base text-[#999] w-[110px] text-center">{{ t('onState') }}</span>
<span class="text-base text-[#999] w-[80px] text-center" v-if="isEdit">{{ t('templateName') }}</span>
</div>
<div ref="fieldBoxRefs" :data-key="payKey">
<div v-for="(childrenItem, childrenIndex) in payItems.pay_type" :key="childrenItem.redio_key"
class="flex table-item-border table-item-pd justify-between" :id="payKey + '_' + childrenIndex">
<div class="table-item-flex w-[150px]">
<span v-if="isEdit" class="iconfont icontuodong mr-2 handle cursor-pointer"></span>
<div class="flex items-center select-none">
<div class="mr-[15px] w-[30px] h-[30px]"><img class="w-[30px]"
:src="img(childrenItem.icon)" /></div>
<span class="text-base text-[#666]">{{ childrenItem.name }}</span>
</div>
</div>
<!-- <div class="table-item-flex w-[110px] select-none">
<el-radio v-model="payItems.default_pay_type" :label="childrenItem.redio_key" size="large" @change="settingDefault(childrenItem,payKey)">{{ t('settingDefault') }}</el-radio>
</div> -->
<div class="table-item-flex w-[110px] justify-center select-none">
<el-switch v-if="isEdit" v-model="childrenItem.status" :active-text="t('isEnable')" @change="enablePaymentMode(childrenItem)"/>
<div v-else>
<el-tag v-if="childrenItem.status" class="ml-2" type="success">{{t('open')}}</el-tag>
<el-tag v-else class="ml-2" type="info">{{t('notOpen')}}</el-tag>
</div>
</div>
<div class="table-item-flex w-[80px] justify-center select-none" v-if="isEdit">
<button @click="configPayFn(childrenItem)" class="text-base" v-if="childrenItem.key !='balancepay'">{{t('clickConfigure')}}</button>
<button v-else>--</button>
</div>
</div>
</div>
</div>
<div class="table-item-flex w-[110px] justify-center select-none">
<el-switch v-if="isEdit" v-model="childrenItem.status" :active-text="t('isEnable')"
@change="enablePaymentMode(childrenItem)" />
<div v-else>
<el-tag v-if="childrenItem.status" class="ml-2" type="success">{{ t('open') }}</el-tag>
<el-tag v-else class="ml-2" type="info">{{ t('notOpen') }}</el-tag>
</div>
</div>
<div class="table-item-flex w-[80px] justify-center select-none" v-if="isEdit">
<button @click="configPayFn(childrenItem)" class="text-base"
v-if="childrenItem.key != 'balancepay'">{{ t('clickConfigure')
}}</button>
<button v-else>--</button>
</div>
</div>
</div>
</div>
</el-card>
<div class="fixed-footer-wrap" v-if="isEdit">
<div class="fixed-footer-wrap" v-if="isEdit">
<div class="fixed-footer">
<el-button type="primary" :loading="loading" @click="cancelFn">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="cancelFn">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="saveFn(formRef)">{{ t('save') }}</el-button>
</div>
</div>
<wechatpay ref="wechatpayDialog" @complete="setConfigInfo" />
<alipay ref="alipayDialog" @complete="setConfigInfo" />
<wechatpay ref="wechatpayDialog" @complete="setConfigInfo" />
<alipay ref="alipayDialog" @complete="setConfigInfo" />
<offlinepay ref="offlinepayDialog" @complete="setConfigInfo" />
</el-card>
</template>
<script lang="ts" setup>
import { ref, watch, nextTick } from 'vue'
import { t } from '@/lang'
import { getPayConfig,getPayList,getPayConfigList,setPatConfig } from '@/api/sys'
import { getPayConfigList, setPatConfig } from '@/api/sys'
import Wechatpay from '@/views/setting/components/pay-wechatpay.vue'
import Alipay from '@/views/setting/components/pay-alipay.vue'
import Offlinepay from '@/views/setting/components/pay-offlinepay.vue'
import { img } from '@/utils/common'
import { ElMessage, ElNotification } from 'element-plus'
import { ElMessage } from 'element-plus'
import Sortable, { SortableEvent } from 'sortablejs'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title;
const pageName = route.meta.title
const wechatpayDialog: Record<string, any> | null = ref(null)
const wechatpayDialog: Record<string, any> | null = ref(null)
const alipayDialog: Record<string, any> | null = ref(null)
const offlinepayDialog: Record<string, any> | null = ref(null)
let payLoading = ref(true);
const payLoading = ref(true)
let isEdit = ref<boolean>(false);
const isEdit = ref<boolean>(false)
const setConfigBtn = ref()
//
//
const payConfigData = ref([])
const checkPayConfigList = () => {
getPayConfigList().then(res => {
let payData = res.data;
for(let i in payData){
let payType = payData[i]['pay_type'];
let pay_type =[];
let default_key = '';
payType.forEach((item,index) => {
item['redio_key'] = payData[i]['key']+"_"+item['key'];
item['defauit_key'] = "";
if(item['is_default'] == 1){
default_key = item['redio_key']
}
getPayConfigList().then(res => {
const payData = res.data
for (const i in payData) {
const payType = payData[i].pay_type
const pay_type = []
let default_key = ''
item['status'] = Boolean(item['status']);
pay_type.push(item)
});
payData[payData[i]['key']]['default_pay_type'] = default_key;
payData[payData[i]['key']]['pay_type'] = pay_type;
}
payConfigData.value = payData;
payLoading.value = false;
nextTick(()=>{
fieldBoxRefs.value.forEach((item,index) => {
sortableFn(item,index);
});
})
payType.forEach((item, index) => {
item.redio_key = payData[i].key + '_' + item.key
item.defauit_key = ''
if (item.is_default == 1) {
default_key = item.redio_key
}
}).catch(() => {
})
item.status = Boolean(item.status)
pay_type.push(item)
})
payData[payData[i].key].default_pay_type = default_key
payData[payData[i].key].pay_type = pay_type
}
payConfigData.value = payData
payLoading.value = false
nextTick(() => {
fieldBoxRefs.value.forEach((item, index) => {
sortableFn(item, index)
})
})
}).catch(() => {
})
}
checkPayConfigList()
//
const setConfigInfo = (data)=>{
payConfigData.value[data.channel].pay_type.forEach(element => {
if(element.key == data.type){
element.config = data.config;
}
});
const setConfigInfo = (data) => {
console.log(data)
payConfigData.value[data.channel].pay_type.forEach(element => {
if (element.key == data.type) {
element.config = data.config
}
})
console.log(payConfigData.value)
}
//
const configPayFn = (data)=>{
eval(data.key+'Dialog.value.setFormData(data)');
eval(data.key+'Dialog.value.showDialog = true;');
const configPayFn = (data) => {
eval(data.key + 'Dialog.value.setFormData(data)')
eval(data.key + 'Dialog.value.showDialog = true;')
}
//
const enablePaymentMode = (res) => {
if(res.key == "wechatpay" && !res?.config?.mch_secret_cert || res.key == "alipay" && !res?.config?.alipay_root_cert_path){
res.status = false
ElMessage(t('configurePaymentMethod'));
return false;
}
if (res.key == 'wechatpay' && !res?.config?.mch_secret_cert || res.key == 'alipay' && !res?.config?.alipay_root_cert_path) {
res.status = false
ElMessage(t('configurePaymentMethod'))
return false
}
}
interface SortableEvt extends SortableEvent {
@ -146,79 +153,81 @@ interface SortableEvt extends SortableEvent {
}
//
const fieldBoxRefs = ref<any>([]);
const fieldBoxRefs = ref<any>([])
watch(isEdit, (newValue, oldValue) => {
if(newValue){
nextTick(()=>{
fieldBoxRefs.value.forEach((item,index) => {
sortableFn(item,index);
});
})
}
});
if (newValue) {
nextTick(() => {
fieldBoxRefs.value.forEach((item, index) => {
sortableFn(item, index)
})
})
}
})
// item=>index
const sortableFn = (item,index)=>{
const sortable = Sortable.create(item, {
group: {
put: false //
},
handle: '.handle',
animation: 200,
disabled: false,
const sortableFn = (item, index) => {
const sortable = Sortable.create(item, {
group: {
put: false //
},
handle: '.handle',
animation: 200,
disabled: false,
onEnd: (evt) => {
let key = evt.target.getAttribute('data-key');
let data = payConfigData.value[key].pay_type;
data.splice(evt.newIndex, 0, ...data.splice(evt.oldIndex, 1));
const key = evt.target.getAttribute('data-key')
const data = payConfigData.value[key].pay_type
data.splice(evt.newIndex, 0, ...data.splice(evt.oldIndex, 1))
}
})
}
//
const saveFn = ()=>{
payLoading.value = true;
let data = JSON.parse(JSON.stringify(payConfigData.value));
Object.values(data).forEach((item,index)=>{
item.pay_type.forEach((subItem, subIndex) => {
subItem.sort = subIndex;
subItem.status = Number(subItem.status);
});
})
const saveFn = () => {
payLoading.value = true
const data = JSON.parse(JSON.stringify(payConfigData.value))
Object.values(data).forEach((item, index) => {
item.pay_type.forEach((subItem, subIndex) => {
subItem.sort = subIndex
subItem.status = Number(subItem.status)
})
})
setPatConfig({config: data}).then(res =>{
checkPayConfigList()
isEdit.value = false;
payLoading.value = false;
})
setPatConfig({ config: data }).then(res => {
checkPayConfigList()
isEdit.value = false
payLoading.value = false
})
}
//
const cancelFn = ()=>{
location.reload()
const cancelFn = () => {
location.reload()
}
</script>
<style lang="scss" scoped>
.table-item-pd{
@apply py-[10px] px-[11px];
.table-item-pd {
@apply py-[10px] px-[11px];
}
.table-item-border{
@apply border-b border-[#ebeef5];
.table-item-border {
@apply border-b border-[#ebeef5];
}
.table-item-flex{
display: flex;
.table-item-flex {
display: flex;
align-items: center;
}
:deep(.box-pay-card) .el-card__body{
padding: 0;
:deep(.box-pay-card) .el-card__body {
padding: 0;
}
.table-bg{
background: #f5f7f9;
.table-bg {
background: #f5f7f9;
}
html.dark .table-bg{
background: #141414;
html.dark .table-bg {
background: #141414;
}
</style>
</style>

View File

@ -14,7 +14,7 @@
<el-table-column prop="version" :label="t('version')" min-width="150" />
<el-table-column prop="create_time" :label="t('createTime')" min-width="150" />
<el-table-column prop="status_name" :label="t('status')" min-width="100" />
<!-- <el-table-column prop="status_name" :label="t('status')" min-width="100" /> -->
<el-table-column :label="t('operation')" fixed="right" width="130">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
@ -33,7 +33,7 @@
<el-form :model="formData" label-width="110px" ref="formRef" :rules="formRules" class="page-form"
v-loading="loading">
<el-form-item :label="t('version')" prop="version">
<el-input v-model="formData.version" :placeholder="t('keywordsPlaceholder')" class="input-width"/>
<el-input v-model="formData.version" :placeholder="t('versionPlaceholder')" class="input-width"/>
</el-form-item>
<el-form-item :label="t('file')" prop="path">
<upload-file v-model="formData.path" class="input-width" api="applet/upload" />
@ -112,7 +112,8 @@ const initialFormData = {
id: 0,
desc: '',
path: '',
verison: ''
version: '',
type: 'weapp'
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
@ -121,7 +122,7 @@ const showEvent = () => {
formData.id = 0,
formData.desc = '',
formData.path = '',
formData.verison = '',
formData.version = '',
showDialog.value = true
}
@ -171,7 +172,7 @@ const editEvent = (item) => {
}
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('cronDeleteTips'), t('warning'),
ElMessageBox.confirm(t('weappVersionDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),

View File

@ -1,11 +1,25 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<div class="flex justify-between items-center mb-[20px]">
<span class="text-[20px]">{{pageName}}</span>
<el-button type="primary" @click="showEvent">{{ t('addCron') }}</el-button>
</div>
<div class="mt-[10px]">
<el-alert class="warm-prompt " type="info">
<template #default>
<div class="flex items-center">
<div>
<p>
{{ t('cronTipsOne') }}
</p>
<p class="mt-2">
{{ t('cronTipsTwo') }}
</p>
</div>
</div>
</template>
</el-alert>
<div class="mt-[20px]">
<el-table :data="cronTableData.data" size="large" v-loading="cronTableData.loading">
<template #empty>
@ -76,7 +90,7 @@
</el-input>
</div>
</el-form-item>
<el-form-item :label="t('openStatus')" >
<el-form-item :label="t('isopen')" >
<div class="input-width flex items-center text-sm">
<el-radio-group v-model="formData.status">
<el-radio :label="1">{{ t('yes') }}</el-radio>
@ -203,22 +217,23 @@ const formRules = computed(() => {
})
const validatePass = (rule: any, value: any, callback: any) => {
if(formData.time.type == 'min' && formData.time.min == ''){
return callback(new Error(t('cronTimeTips')))
if(formData.time.type == 'min' && formData.time.min != ''){
return callback()
}
if(formData.time.type == 'week' && formData.time.week == '' && formData.time.hour == '' && formData.time.min == ''){
return callback(new Error(t('cronTimeTips')))
if(formData.time.type == 'week' && formData.time.week != '' && formData.time.hour != '' && formData.time.min != ''){
return callback()
}
if(formData.time.type == 'month' && formData.time.day == '' && formData.time.hour == '' && formData.time.min == ''){
return callback(new Error(t('cronTimeTips')))
if(formData.time.type == 'month' && formData.time.day != '' && formData.time.hour != '' && formData.time.min != ''){
return callback()
}
if(formData.time.type == 'day' && formData.time.day == '' && formData.time.hour == '' && formData.time.min == ''){
return callback(new Error(t('cronTimeTips')))
if(formData.time.type == 'day' && formData.time.day != '' && formData.time.hour != '' && formData.time.min != ''){
return callback()
}
if(formData.time.type == 'hour' && formData.time.hour == '' && formData.time.min == ''){
return callback(new Error(t('cronTimeTips')))
if(formData.time.type == 'hour' && formData.time.hour != '' && formData.time.min != ''){
return callback()
}
return callback()
return callback(new Error(t('cronTimeTips')))
}
const save_type = ref(false)
const addEvent = async (formEl: FormInstance | undefined) => {