Merge branch 'dev-yxl' of gitee.com:zongzhige/shopxo-diy into dev

This commit is contained in:
gongfuxiang
2025-10-25 23:35:49 +08:00
9 changed files with 98 additions and 195 deletions

View File

@ -1,20 +1,20 @@
import { get_type } from '@/utils/common';
import request from '@/utils/request';
import index_request from '@/utils/index-request';
import { isEmpty } from 'lodash';
class CommonAPI {
/** 链接初始化接口 */
static getInit() {
const new_type = get_type();
if (isEmpty(new_type)) {
const location_host = document.location.host;
if (isEmpty(new_type) || location_host.indexOf('#/tabbar') !== -1) {
return request({
url: `diyapi/init`,
method: 'post',
});
} else {
return index_request({
url: `?s=plugins/index/pluginsname/${ new_type }/pluginscontrol/diyapi/pluginsaction/init.html`,
return request({
url: `plugins/index/pluginsname/${ new_type }/pluginscontrol/diyapi/pluginsaction/init.html`,
method: 'post',
});
}

View File

@ -1,22 +0,0 @@
import request from '@/utils/request';
class ShopAPI {
/** 获取tabbar列表 */
static getTabbar(data: any) {
return request({
url: `diyapi/apptabbardata`,
method: 'post',
data,
});
}
/** 保存tabbar */
static saveTabbar(data: any) {
return request({
url: `diyapi/apptabbarsave`,
method: 'post',
data,
});
}
}
export default ShopAPI;

View File

@ -46,6 +46,7 @@ export const commonStore = defineStore('common', () => {
diy_upload_url: '' as string, //diy导入
attachment_category_operate: {} as any, // 附件分类权限
attachment_operate: {} as any, // 附件权限
diy_config_operate: {} as any, //---- diy配置权限
} as any, // 基础数据配置参数
preview_url: '',
});

View File

@ -15,46 +15,89 @@ export const font_weight = [
{ name: '正常', value: '400' },
];
/**
* 从URL中提取指定前缀后的ID值
* @param prefix 前缀字符串
* @returns 提取的ID值
*/
const extractIdFromUrl = (prefix: string): string => {
const url = document.location.href;
const startIndex = url.indexOf(prefix);
if (startIndex === -1) return '';
// 计算起始位置(包含前缀)
const start = startIndex + prefix.length;
let result = url.substring(start);
// 移除.html后缀
const dotIndex = result.indexOf('.');
if (dotIndex !== -1) {
result = result.substring(0, dotIndex);
}
// 移除查询参数
const andIndex = result.indexOf('&');
if (andIndex !== -1) {
result = result.substring(0, andIndex);
}
// 移除路径分隔符后的部分
const slashIndex = result.indexOf('/');
if (slashIndex !== -1) {
result = result.substring(0, slashIndex);
}
return result;
};
// 截取document.location.search字符串内id/后面的所有字段
export const get_id = () => {
let new_id = '';
// 去除origin的数据
// 先尝试匹配 id/ 模式
const url = document.location.href;
if (url.indexOf('id/') != -1) {
new_id = url.substring(url.indexOf('id/') + 3);
// 去除字符串的.html
let html_index = new_id.indexOf('.html');
if (html_index != -1) {
new_id = new_id.substring(0, html_index);
const idIndex = url.indexOf('id/');
if (idIndex !== -1) {
const result = url.substring(idIndex + 3);
const htmlIndex = result.indexOf('.html');
if (htmlIndex !== -1) {
return result.substring(0, htmlIndex);
}
return new_id;
} else if (url.indexOf('-saveinfo') != -1) {
new_id = url.substring(url.indexOf('-saveinfo-') + 10);
// 去除字符串的.html
const dot_data = new_id.split('.')[0];
if (dot_data != '') {
new_id = dot_data.split('/')[0];
}
return new_id;
} else {
return new_id;
return result.split('.')[0].split('/')[0];
}
// 尝试匹配-saveinfo-模式
const saveinfoResult = extractIdFromUrl('-saveinfo-');
if (saveinfoResult) return saveinfoResult;
// 尝试匹配-forminputinfo-模式
return extractIdFromUrl('-diyinfo-');
};
// 获取当前业务类型
export const get_type = () => {
let new_type = '';
return data_handle('type/', '');
}
// 获取类型
export const get_business = () => {
return data_handle('business/', '');
}
// 数据处理
export const data_handle = (val: string, default_val: string): string => {
let new_data = default_val;
// 去除origin的数据
const url = document.location.href;
if (url.indexOf('type/') != -1) {
new_type = url.substring(url.indexOf('type/') + 5);
if (url.indexOf(val) != -1) {
new_data = url.substring(url.indexOf(val) + val.length);
// 去除字符串的.html
const dot_data = new_type.split('.')[0];
if (dot_data != '') {
new_type = dot_data.split('/')[0];
// 去除并且数据
if (new_data.indexOf('&') != -1) {
new_data = new_data.split('&')[0];
}
return new_type;
const dot_data = new_data.split('.')[0];
if (dot_data != '') {
new_data = dot_data.split('/')[0];
}
return new_data;
} else {
return new_type;
return new_data;
}
}

View File

@ -1,118 +0,0 @@
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import { ElMessage, ElMessageBox, type MessageHandler } from 'element-plus';
import { get_cookie } from './index';
import { get_id, get_type } from './common';
// 提示拦截
let messageInstance: MessageHandler;
const message_error = (info: string) => {
if (messageInstance) {
messageInstance.close();
}
messageInstance = ElMessage.error({
type: 'error',
message: info,
duration: 30000,
showClose: true,
});
};
// 创建一个状态变量来跟踪是否已经弹出了退出登录的弹窗
const isLogoutModalShown = ref(true);
// 用于存储每个请求的CancelToken
const pendingRequests = new Map();
// 不需要认证的接口
const release_url = ['attachmentapi/upload'];
// 创建 axios 实例
const index = window.location.href.lastIndexOf('?s=');
const new_data = window.location.href.substring(0, index);
const new_index = new_data.lastIndexOf('/');
const pro_url = window.location.href.substring(0, new_index);
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API_INDEX_PHP == '/dev-index' ? import.meta.env.VITE_APP_BASE_API_INDEX_PHP : pro_url + '/index.php',
timeout: 60000,
headers: { 'Content-Type': 'application/json;charset=utf-8', 'X-Requested-With': 'XMLHttpRequest' },
});
/** @ts-ignore */
// 请求拦截器
service.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
// 如果是本地则使用静态tonken如果是线上则使用cookie的token
const symbol = config.url?.includes('?') ? '&' : '?';
if (import.meta.env.VITE_APP_BASE_API_INDEX_PHP == '/dev-index') {
let temp_data = await import(import.meta.env.VITE_APP_BASE_API_INDEX_PHP == '/dev-index' ? '../../temp.d' : '../../temp_pro.d');
config.url = config.url + symbol + 'token=' + temp_data.default.temp_token;
} else {
// 如果是shop认为是多商户插件使用user_info的cookie
const cookie = get_type() == 'shop' ? get_cookie('user_info') : get_cookie('admin_info');
if (cookie && cookie !== null && cookie !== 'null') {
config.url = config.url + '&token=' + (JSON.parse(cookie) !== 'null' ? JSON.parse(cookie)?.token : '');
}
}
// 添加diy_id和diy_type参数
config.url = `${config.url}&diy_id=${ get_id() }&diy_type=${ get_type() }`;
// 判断是否是包含不需要认证的接口
const release_list = release_url.filter(item => config.url?.includes(item));
if (release_list.length === 0) {
// 检查是否有相同请求正在进行,如果有则取消, 防止重复请求导致返回数据有误
if (pendingRequests.has(config.url)) {
const cancelToken = pendingRequests.get(config.url);
cancelToken.cancel('canceled');
pendingRequests.delete(config.url);
}
// 创建一个新的 CancelToken
const source = axios.CancelToken.source();
config.cancelToken = source.token;
pendingRequests.set(config.url, source);
}
return config;
},
(error: any) => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
// 请求完成后从pendingRequests中移除
pendingRequests.delete(response.config.url);
const { code, msg, message, data } = response.data;
if (code == 0) {
return response.data;
} else if (code == -400) {
if (isLogoutModalShown.value) {
isLogoutModalShown.value = false;
ElMessageBox.alert(msg, '温馨提示', {
confirmButtonText: '确定',
showClose: false,
type: 'warning',
}).then(() => {
localStorage.clear(); // @vueuse/core 自动导入
window.location.href = data.logout;
});
}
} else {
message_error(msg || message || '系统出错');
return Promise.reject('Error');
// return Promise.reject(new Error(msg || 'Error'));
}
},
(error: any) => {
if (error.response && error.response.data) {
const { msg, message } = error.response.data;
message_error(msg || message || '系统出错');
} else if (error.message == 'canceled') {
console.log('请求已取消');
} else {
message_error(error.message);
}
return Promise.reject(error.message);
}
);
// 导出 axios 实例
export default service;

View File

@ -24,8 +24,11 @@ const pendingRequests = new Map();
// 不需要认证的接口
const release_url = ['attachmentapi/upload'];
// 创建 axios 实例
const index = window.location.href.lastIndexOf('?s=');
const pro_url = window.location.href.substring(0, index);
const index = window.location.href.lastIndexOf('.php');
let pro_url = window.location.origin;
if (index !== -1) {
pro_url = window.location.href.substring(0, index) + '.php';
}
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API == '/dev-admin' ? import.meta.env.VITE_APP_BASE_API : pro_url + '?s=',
timeout: 60000,

View File

@ -5,19 +5,19 @@
<!-- <icon name="arrow-left" color="f">返回</icon> -->
<div class="flex-row align-c">
<div class="name">
<div class="flex-row align-c gap-10 c-pointer" @click="dialog_visible = true">
<image-empty :src="modelValue.logo" class="round img" error-img-style="width: 2.2rem;height: 2.2rem;" />
<div :class="[{'c-pointer': common_store_config?.diy_config_operate?.is_base_data == 1}, 'flex-row align-c gap-10']" @click="common_store_config?.diy_config_operate?.is_base_data == 1 ? dialog_visible = true : ''">
<image-empty v-if="modelValue.logo" :src="modelValue.logo" class="round img" error-img-style="width: 2.2rem;height: 2.2rem;" />
<div>{{ modelValue.name }}</div>
<icon name="edit" color="#7DBEFF"></icon>
<icon v-if="common_store_config?.diy_config_operate?.is_base_data == 1" name="edit" color="#7DBEFF"></icon>
</div>
</div>
</div>
</div>
<div class="nav-right">
<el-button class="btn-plain" @click="upload_manage">上传管理</el-button>
<el-button class="btn-plain" :class="saveDisabled ? 'disabled' : ''" :disabled="saveDisabled" @click="preview_event">预览</el-button>
<el-button class="btn-plain" :class="saveDisabled ? 'disabled' : ''" :disabled="saveDisabled" @click="save_event">保存</el-button>
<el-button class="btn-white" :class="saveDisabled ? 'disabled' : ''" :disabled="saveDisabled" @click="save_close_event">保存关闭</el-button>
<el-button v-if="common_store_config?.diy_config_operate?.is_upload_admin == 1" class="btn-plain" @click="upload_manage">上传管理</el-button>
<el-button v-if="common_store_config.preview_url !== ''" class="btn-plain" :class="saveDisabled ? 'disabled' : ''" :disabled="saveDisabled" @click="preview_event">预览</el-button>
<el-button v-if="common_store_config.diy_config_operate.is_save_button == 1" :class="[common_store_config.diy_config_operate.is_save_close_button == 1 ? 'btn-plain' : 'btn-white', saveDisabled ? 'disabled' : '']" :disabled="saveDisabled" @click="save_event">保存</el-button>
<el-button v-if="common_store_config.diy_config_operate.is_save_close_button == 1" class="btn-white" :class="saveDisabled ? 'disabled' : ''" :disabled="saveDisabled" @click="save_close_event">保存关闭</el-button>
</div>
</div>
<el-dialog v-model="dialog_visible" class="radius-lg" width="650" draggable :close-on-click-modal="false" append-to-body>
@ -53,6 +53,8 @@
</template>
<script setup lang="ts">
import { FormInstance, FormRules } from 'element-plus';
import { commonStore } from '@/store';
const common_store = commonStore();
const props = defineProps({
saveDisabled: {
type: Boolean,
@ -60,6 +62,8 @@ const props = defineProps({
},
});
const modelValue = defineModel({ type: Object, default: {} });
// 公共配置项
const common_store_config = computed(() => common_store.common.config);
// #region 变量 --------------------start
const is_custom_dialog = ref(false);
const dialog_visible = ref(false);

View File

@ -523,7 +523,9 @@ const save_formmat_form_data = (data: diy_data_item, close: boolean = false, is_
preview_dialog.value = true;
diy_id.value = String(res.data);
}
form.value.id = String(res.data);
if (res.data != null && res.data != '') {
form.value.id = String(res.data);
}
// 本地的时候会补id参数
if (import.meta.env.VITE_APP_BASE_API == '/dev-admin') {
history.pushState({}, '', '?s=diy/saveinfo/id/' + res.data + '.html');

View File

@ -24,6 +24,7 @@ import defaultSettings from './components/main/index';
import { cloneDeep } from 'lodash';
import CommonAPI from '@/api/common';
import { commonStore } from '@/store';
import { get_business } from '@/utils/common';
const common_store = commonStore();
const temp_form = ref(defaultSettings.footer_nav);
const form = ref<any>({});
@ -37,7 +38,7 @@ onMounted(() => {
});
const is_empty = ref(false);
const init = () => {
CommonAPI.getDynamicApi(common_store.common.config.app_tabbar_data_url, { type: get_type() })
CommonAPI.getDynamicApi(common_store.common.config.app_tabbar_data_url, { business: get_business() })
.then((res: any) => {
if (res.data) {
let data = res.data;
@ -77,8 +78,8 @@ const save_disabled = ref(false);
const save_event = () => {
const clone_form = cloneDeep(form.value);
const new_data = {
type: get_type(),
config: clone_form,
business: get_business(),
};
save_disabled.value = true;
// 数据改造
@ -93,18 +94,7 @@ const save_event = () => {
save_disabled.value = false;
});
};
const get_type = () => {
let new_type = 'home';
if (document.location.search.indexOf('/type/') != -1) {
new_type = document.location.search.substring(document.location.search.indexOf('/type/') + 6);
// 进行3次切割选择参数内容
const result1 = splitAndGetFirst(new_type, '/');
const result2 = splitAndGetFirst(result1, '&');
return splitAndGetFirst(result2, '#');
} else {
return new_type;
}
};
function splitAndGetFirst(str: string, separator: string): string {
const data = str.split(separator);
if (data.length > 1) {