20260711173006

This commit is contained in:
oneao committed 2026-07-11 17:30:09 +08:00
1 parent d080a2a48d
commit 446758b2fe
384 files changed
+153926 -5937

No files matched your search

View File
Whitespace-only changes.
+43
View File
@@ -0,0 +1,43 @@
import { ElMessage } from "element-plus";
import { ref } from "vue";
import { useUserStore } from "@/store/user";
import axios from "@/utils/axios";
const saving = ref(false);
const error = ref(null);
export function morePermission(permissionId) {
const userStore = useUserStore();
return !userStore.hasActionPermission(permissionId);
}
export async function getQxSql(moduleId, userId) {
saving.value = true;
error.value = "";
try {
const response = await axios.get("/getQxSql", {
params: {
moduleId, // 必填
userId, // 可选
},
});
if (response.data?.success) {
return response.data.data;
} else {
throw new Error(response.data?.message || "生成id失败");
}
} catch (err) {
error.value = `生成id: ${err.message}`;
ElMessage.error(error.value);
return null;
} finally {
saving.value = false;
}
}
// 默认导出所有方法
export default {
morePermission,
getQxSql,
};
+33
View File
@@ -0,0 +1,33 @@
// src/utils/axios.js
import axios from "axios";
import { useUserStore } from "@/store";
const instance = axios.create({
// baseURL: "http://localhost:8089/api", // 后端接口地址
baseURL: 'http://192.168.1.137:8089/api', // 后端接口地址
// baseURL: "http://192.168.1.137:8089/api",
// baseURL: '/api', // 后端接口地址
timeout: 30000,
});
// 请求拦截器:自动添加 token
instance.interceptors.request.use(
(config) => {
const userStore = useUserStore();
if (userStore.isLogined()) {
config.headers.Authorization = `Bearer ${userStore.userInfo.token}`;
}
return config;
},
(error) => Promise.reject(error),
);
// 响应拦截器:统一处理错误或过期 token
instance.interceptors.response.use(
(response) => response,
(error) => {
return Promise.reject(error);
},
);
export default instance;
+141
View File
@@ -0,0 +1,141 @@
import Big from "big.js";
import { formatToDate } from "./time";
/**
* 根据 format 判断列对齐方式
* @param {string} format
*/
export function getAlignByFormat(format) {
if (!format || typeof format !== "string") return "left";
const fmt = format.trim().toLowerCase();
if (fmt.startsWith("money") || fmt.startsWith("number")) {
return "right";
}
if (fmt === "checkbox") {
return "center";
}
return "left";
}
/**
* 根据 format 格式化值
* @param {any} value 原始值
* @param {string} format 格式描述,如 money(2)、percent(1)
*/
export function formatValueByFormat(value, format) {
if (value === null || value === undefined || value === "") return "";
if (!format || typeof format !== "string") return value;
const fmt = format.trim().toLowerCase();
// money(n)
if (fmt.startsWith("money")) {
const match = fmt.match(/^money\((\d+)\)$/);
const decimals = match ? Number(match[1]) : 2;
const num = Number.parseFloat(value);
if (Number.isNaN(num)) return value;
// 使用Intl.NumberFormat来处理千分位
return new Intl.NumberFormat("en-US", {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
}).format(num);
} else if (fmt.startsWith("date")) {
return formatToDate(value);
} else if (fmt.startsWith("number")) {
return Number(value) || 0;
}
return value;
}
/**
* 根据 format 格式化值
* @param {any} value 原始值
* @param {string} format 格式描述,如 money(2)、percent(1)
*/
export function formatValueByFullFormat(value, format) {
if (value === null || value === undefined || value === "") return "";
if (!format || typeof format !== "string") return value;
const fmt = format.trim().toLowerCase();
// money(n)
if (fmt.startsWith("money")) {
const match = fmt.match(/^money\((\d+)\)$/);
const decimals = match ? Number(match[1]) : 2;
const num = Number.parseFloat(value);
if (Number.isNaN(num)) return value;
return num.toFixed(decimals);
} else if (fmt.startsWith("date")) {
return formatToDate(value);
} else if (fmt.startsWith("number")) {
return Number(value) || 0;
}
return value;
}
export function updateTableFooterData(columns, tableData, includeSeq = true) {
// 初始化合计数据
const footerData = columns.reduce((acc, col) => {
// 如果当前列有子列,递归处理子列的合计
if (col.children && col.children.length > 0) {
// 递归计算子列的合计数据
const childFooterData = updateTableFooterData(col.children, tableData, false);
// 将子列的合计合并到父列中
childFooterData.forEach((childData) => {
for (const [key, value] of Object.entries(childData)) {
if (!acc[key]) {
acc[key] = new Big(0);
}
acc[key] = acc[key].plus(new Big(value));
}
});
} else {
// 只有showSummary为'1'的列才参与合计
if (col.showSummary === "1") {
// 计算当前列的合计
const total = tableData.reduce((sum, row) => {
let value = row[col.prop] || 0;
// 使用 Big 来避免浮动点问题
return sum.plus(new Big(value));
}, new Big(0));
// 将未格式化的合计结果存入acc
acc[col.prop] = total;
}
}
return acc;
}, {});
for (const [key, value] of Object.entries(footerData)) {
const column = columns.find((col) => col.prop === key); // 找到对应的列信息
if (column) {
footerData[key] = formatValueByFullFormat(value, column.displayFormat);
}
}
// 返回合计结果
if (includeSeq) {
return [
{
seq: "合计",
...footerData,
},
];
} else {
return [footerData];
}
}
+57
View File
@@ -0,0 +1,57 @@
import { ElMessage } from "element-plus";
import { ref } from "vue";
import axios from "@/utils/axios";
const saving = ref(false);
const error = ref(null);
export async function getSFId() {
saving.value = true;
error.value = "";
try {
const response = await axios.get("/getSFId");
if (response.data?.success) {
return String(response.data.data);
} else {
throw new Error(response.data?.message || "生成id失败");
}
} catch (err) {
error.value = `生成id: ${err.message}`;
ElMessage.error(error.value);
return null;
} finally {
saving.value = false;
}
}
export async function getAutoId(moduleId, userId) {
saving.value = true;
error.value = "";
try {
const response = await axios.get("/getAutoId", {
params: {
moduleId, // 必填
userId, // 可选
},
});
if (response.data?.success) {
return response.data.data;
} else {
throw new Error(response.data?.message || "查询失败");
}
} catch (err) {
error.value = `查询失败: ${err.message}`;
ElMessage.error(error.value);
return null;
} finally {
saving.value = false;
}
}
// 默认导出所有方法
export default {
getSFId,
getAutoId,
};
@@ -0,0 +1,471 @@
/* 比对并分类数据变更
* @param {Array} currentData 变更后的数据数组
* @param {Array} originalData 原始数据数组
* @param {String} primaryKey 主键字段名
* @param {Array} ignoreFields 过滤字段(可传可不传)
* @returns {Object} 包含分类结果的对象 { insertdata: [], updatedata: [], deletedata: [], hasChanges: Boolean }
*/
export function compareAndClassifyData(currentData, originalData, primaryKey) {
const versionFields = ["mx_version", "b_version"];
const getVersionFields = (row) => {
const fields = {};
versionFields.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(row, key)) {
fields[key] = row[key] ?? "";
}
});
return fields;
};
const result = {
inserts: [],
updates: [],
deletes: [],
hasChanges: false, // 默认无变动
};
// 1. 找出新增的数据
result.inserts = currentData.filter(
(row) =>
!row[primaryKey] ||
!originalData.some((originalRow) => originalRow[primaryKey] === row[primaryKey]),
);
// 2. 找出修改的数据
result.updates = currentData.reduce((acc, row) => {
const rowId = row[primaryKey];
if (!rowId) return acc;
const originalRow = originalData.find((r) => r[primaryKey] === rowId);
if (!originalRow) return acc;
const changedFields = {};
Object.keys(row).forEach((key) => {
if (key !== primaryKey && !key.startsWith("v_") && !versionFields.includes(key)) {
const oldVal = originalRow[key];
const newVal = row[key];
const normalize = (v) => (v === null || v === undefined ? "" : v);
if (normalize(newVal) !== normalize(oldVal)) {
// ✅ 避免 undefined 被丢弃
changedFields[key] = newVal ?? "";
}
}
});
// Object.keys(row).forEach((key) => {
// if (key !== primaryKey && !isEqual(row[key], originalRow[key])) {
// if (!key.startsWith('v_')) {
// changedFields[key] = row[key]
// }
// }
// })
if (Object.keys(changedFields).length > 0) {
acc.push({
[primaryKey]: rowId,
...getVersionFields(row),
...changedFields,
});
}
return acc;
}, []);
// 3. 找出删除的数据
result.deletes = originalData.filter(
(originalRow) => !currentData.some((row) => row[primaryKey] === originalRow[primaryKey]),
);
// 4. 是否有任何变动
result.hasChanges =
result.inserts.length > 0 || result.updates.length > 0 || result.deletes.length > 0;
return result;
}
/**
* 在数组对象的每个元素中覆盖式填入 JSON 对象的值
* @param {Array} arr - 目标数组(将被修改)
* @param {object} json - 包含要添加/覆盖的键值对的 JSON 对象
* @returns {Array} 修改后的数组
* @throws {Error} 当参数类型不正确时抛出错误
*/
// export function fillDefaultArray(arr, json) {
// // 基本数据验证
// if (!Array.isArray(arr))
// throw new Error('第一个参数必须是数组')
// if (typeof json !== 'object' || json === null || Array.isArray(json)) {
// throw new Error('第二个参数必须是普通对象(JSON对象)')
// }
// // 覆盖式填充每个元素
// return arr.map((item) => {
// // 只处理对象元素
// if (typeof item === 'object' && item !== null) {
// return { ...item, ...json } // 覆盖已有属性
// }
// return item // 非对象元素保持不变
// })
// }
export function fillDefaultArray(arr, json) {
// 基本数据验证
if (!Array.isArray(arr)) throw new Error("第一个参数必须是数组");
if (typeof json !== "object" || json === null || Array.isArray(json)) {
throw new Error("第二个参数必须是普通对象(JSON对象)");
}
// 原地修改数组中的每个对象
for (const item of arr) {
if (typeof item === "object" && item !== null) {
Object.assign(item, json);
}
}
// 可选:方便链式调用
return arr;
}
// export function fillSerialNoAndDefaults(arr, defaults = {}) {
// if (!Array.isArray(arr)) return;
// arr.forEach((item, index) => {
// if (typeof item === "object" && item !== null) {
// item.mx_xh = index + 1;
// Object.assign(item, defaults);
// }
// });
// }
export function fillSerialNoAndDefaults(arr, defaults = {}) {
if (!Array.isArray(arr)) return;
arr.forEach((item, index) => {
if (typeof item === "object" && item !== null) {
item.mx_xh = index + 1;
Object.entries(defaults).forEach(([key, value]) => {
item[key] = typeof value === "function" ? value(item, index) : value;
});
}
});
}
export function compareAndClassifyData1(currentData, originalData, primaryKey, ignoreFields = []) {
const ignoreSet = new Set(ignoreFields);
const result = {
insertdata: [],
updatedata: [],
deletedata: [],
hasChanges: false,
};
// 工具函数:剔除不需要的字段
const omitFields = (row) => {
const res = {};
Object.keys(row).forEach((key) => {
if (key !== primaryKey && ignoreSet.has(key)) return;
res[key] = row[key];
});
return res;
};
// 1️⃣ 新增
result.insertdata = currentData
.filter(
(row) => !row[primaryKey] || !originalData.some((o) => o[primaryKey] === row[primaryKey]),
)
.map((row) => omitFields(row)); // 在插入时过滤字段
// 2️⃣ 修改
result.updatedata = currentData.reduce((acc, row) => {
const rowId = row[primaryKey];
if (!rowId) return acc;
const originalRow = originalData.find((r) => r[primaryKey] === rowId);
if (!originalRow) return acc;
const changedFields = {};
Object.keys(row).forEach((key) => {
if (key === primaryKey || ignoreSet.has(key)) return; // 忽略指定字段
if (row[key] !== originalRow[key]) changedFields[key] = row[key];
});
if (Object.keys(changedFields).length > 0) {
acc.push({
[primaryKey]: rowId,
...omitFields(changedFields), // 在更新时也过滤字段
});
}
return acc;
}, []);
// 3️⃣ 删除,只保留 primaryKey
result.deletedata = originalData
.filter((o) => !currentData.some((c) => c[primaryKey] === o[primaryKey]))
.map((row) => ({ [primaryKey]: row[primaryKey] }));
// 4️⃣ 是否有变动
result.hasChanges =
result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0;
return result;
}
/**
* 深度比较两个值是否相等
* @param {*} a 第一个值
* @param {*} b 第二个值
* @returns {boolean} 是否相等
*/
export function isEqual(a, b) {
// 处理基本类型和null/undefined
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (typeof a !== "object" || a === null || b === null) {
return a === b;
}
// 处理数组
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false;
return a.every((item, i) => isEqual(item, b[i]));
}
// 处理对象
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every((key) => isEqual(a[key], b[key]));
}
/**
* 检测并提取变更的字段(跳过v_开头的字段)
* @param {object} currentData 当前数据
* @param {object} originalData 原始数据
* @param {string} primaryKey 主键字段名
* @param {string} primaryKeyValue 主键值
* @returns {object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Object }
*/
export function detectChanges(currentData, originalData, primaryKey, primaryKeyValue) {
const updateValue = {};
// 遍历所有字段检测变更
Object.keys(currentData).forEach((key) => {
// 跳过以v_开头的字段
if (key.startsWith("v_")) return;
if (key === "b_type") {
const currentStr = normalizeOwnerCustomer(currentData[key]);
const originalStr = normalizeOwnerCustomer(originalData[key]);
if (currentStr !== originalStr) {
updateValue[key] = currentStr; // 保存时格式化为逗号分隔字符串
}
} else {
// 比较前,先将 undefined 转换为空字符串
const currentValue = currentData[key] === undefined ? "" : currentData[key];
const originalValue = originalData[key] === undefined ? "" : originalData[key];
// 使用JSON.stringify进行深度比较
if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) {
updateValue[key] = currentValue;
}
}
});
// 如果没有有效变更
if (Object.keys(updateValue).length === 0) {
return { hasChanges: true, updateData: [] };
}
// 返回变更数据和主键
return {
hasChanges: true,
updateData: [
{
[primaryKey]: primaryKeyValue,
...updateValue,
},
],
};
}
/**
* 检测并提取变更的字段(跳过v_开头的字段和指定排除字段)
* @param {object} currentData 当前数据
* @param {object} originalData 原始数据
* @param {Array|string} primaryKeys 主键字段名或数组
* @param {Array} excludeKeys 可选,排除的字段数组
* @returns {object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Array }
*/
export function detectChangesFields(currentData, originalData, primaryKeys, excludeKeys = []) {
const updateValue = {};
Object.keys(currentData).forEach((key) => {
// 跳过以v_开头的字段
if (key.startsWith("v_")) return;
// 跳过排除字段
if (excludeKeys.includes(key)) return;
const currentValue = currentData[key] === undefined ? "" : currentData[key];
const originalValue = originalData[key] === undefined ? "" : originalData[key];
if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) {
updateValue[key] = currentValue;
}
});
if (Object.keys(updateValue).length === 0) {
return { hasChanges: false, updateData: [] };
}
// 构建主键字段对象
const primaryKeyObj = {};
if (Array.isArray(primaryKeys)) {
primaryKeys.forEach((key) => {
primaryKeyObj[key] = originalData[key];
});
} else {
primaryKeyObj[primaryKeys] = originalData[primaryKeys];
}
return {
hasChanges: true,
updateData: [
{
...primaryKeyObj,
...updateValue,
},
],
};
}
function normalizeOwnerCustomer(val) {
if (typeof val === "string") {
return val.split(",").filter(Boolean).join(","); // '001,002,003'
}
if (Array.isArray(val)) {
return val.map((item) => (Array.isArray(item) ? item[0] : item)).join(",");
}
return "";
}
/**
* 统一数据差异比对(主表 / 子表通用)
*
* @param {Array | object} current 当前数据
* @param {Array | object} original 原始数据
* @param {string | Array} primaryKey 主键
* @param {object} options
* @param {Array} options.ignoreFields 忽略字段(如 v_ 字段)
* @param {boolean} options.isSingle 是否单条主表
*/
export function diffData(
current,
original,
primaryKey,
{ ignoreFields = [], isSingle = false } = {},
) {
const ignoreSet = new Set(ignoreFields);
const result = {
hasChanges: false,
insertdata: [],
updatedata: [],
deletedata: [],
};
// ===========================
// 单表处理
// ===========================
if (isSingle) {
const isNewRecord = !original || Object.keys(original).length === 0 || !original[primaryKey];
// 新增
if (isNewRecord) {
result.hasChanges = true;
result.insertdata.push(JSON.parse(JSON.stringify(current)));
return result;
}
// 修改
const changes = {};
Object.keys(current).forEach((key) => {
if (key.startsWith("v_") || ignoreSet.has(key)) return;
const cur = current[key] ?? "";
const org = original[key] ?? "";
if (JSON.stringify(cur) !== JSON.stringify(org)) {
changes[key] = cur;
}
});
if (Object.keys(changes).length > 0) {
result.hasChanges = true;
result.updatedata.push({ [primaryKey]: current[primaryKey], ...changes });
}
return result;
}
// ===========================
// 子表处理(优化 Map 提升性能)
// ===========================
const pk = Array.isArray(primaryKey) ? primaryKey[0] : primaryKey;
const originalMap = new Map(original.map((item) => [item[pk], item]));
// 新增
result.insertdata = current.filter((row) => !row[pk] || !originalMap.has(row[pk]));
// 修改
result.updatedata = [];
current.forEach((row) => {
const id = row[pk];
if (!id) return;
const oldRow = originalMap.get(id);
if (!oldRow) return;
const changes = {};
Object.keys(row).forEach((key) => {
if (key === pk || key.startsWith("v_") || ignoreSet.has(key)) return;
const cur = row[key] ?? "";
const org = oldRow[key] ?? "";
if (JSON.stringify(cur) !== JSON.stringify(org)) {
changes[key] = cur;
}
});
if (Object.keys(changes).length > 0) {
result.updatedata.push({ [pk]: id, ...changes });
}
});
// 删除
result.deletedata = original
.filter((o) => !current.some((c) => c[pk] === o[pk]))
.map((o) => ({ [pk]: o[pk] }));
result.hasChanges =
result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0;
return result;
}
// 默认导出所有方法
export default {
isEqual,
compareAndClassifyData,
detectChanges,
fillDefaultArray,
diffData,
fillSerialNoAndDefaults,
};
+59
View File
@@ -0,0 +1,59 @@
import { ref } from "vue";
import axios from "@/utils/axios";
import { ElMessageBox } from "element-plus";
const saving = ref(false);
const error = ref(null);
export async function delBefore(moduleId, id) {
try {
saving.value = true;
error.value = "";
const response = await axios.post(
"/delBefore",
{ moduleId, id },
{
headers: { "Content-Type": "application/json" },
},
);
//console.log('response=-=-=-=-=-')
//console.log(response)
return response.data;
} catch (err) {
error.value = `${err.message}`;
return { success: false, message: err.message };
} finally {
saving.value = false;
}
}
export async function confirmDelete(rows) {
const count = rows.length;
let msg = "确定要删除吗?";
if (count == 1) {
let id = rows[0].b_id || rows[0].subid || "";
msg = "确定要删除 " + id + " 吗?";
}
try {
await ElMessageBox.confirm(count > 1 ? `确定要删除选中的 ${count} 条数据吗?` : msg, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
return true;
} catch (e) {
if (e === "cancel" || e === "close") {
return false;
}
throw e; // 真异常继续抛
}
}
export default {
delBefore,
confirmDelete,
};
// 导出单例
+129
View File
@@ -0,0 +1,129 @@
import axios from "@/utils/axios";
export async function getDataBySql(sql) {
try {
const response = await axios.get("/selectlistbysql", {
params: { sql }, // GET 请求参数
});
if (response.data?.success) {
return response.data.data;
} else {
console.error("查询失败:", response.data?.message);
return [];
}
} catch (err) {
console.error("接口异常:", err);
return [];
}
}
export async function getDataBySqlPage(sql, pkColumn, page, size) {
try {
const response = await axios.get("/selectlistbysqlpage", {
params: {
sql,
pkColumn,
page,
size,
},
});
if (response.data?.success) {
return response.data.data;
} else {
console.error("查询失败:", response.data?.message);
return [];
}
} catch (err) {
console.error("接口异常:", err);
return [];
}
}
// const result = await getDataBySqlPageMapping(buildSql(), props.moduleId, props.userId, selectOrder.value, pagination.currentPage, pagination.pageSize)
export async function getDataBySqlPageMapping(sql, moduleId, userId, orderby, page, size) {
try {
const response = await axios.post("/selectlistbysqlpagemapping", {
sql,
moduleId,
userId,
orderby,
page,
size,
});
if (response.data?.success) {
return response.data.data;
} else {
console.error("查询失败:", response.data?.message);
return [];
}
} catch (err) {
console.error("接口异常:", err);
return [];
}
}
export async function getMultipleDataBySql(sql, return_value, return_value2) {
try {
const response = await axios.get("/multipleSelectlistbysql", {
params: { sql, return_value, return_value2 }, // GET 请求参数
});
if (response.data?.success) {
return response.data.data;
} else {
console.error("查询失败:", response.data?.message);
return [];
}
} catch (err) {
console.error("接口异常:", err);
return [];
}
}
export async function loaddataApi(viewname, searchcondition) {
const { data } = await axios.get("/pagdetail", {
params: {
tableName: viewname,
whereClause: searchcondition,
},
});
if (!data?.success) {
throw new Error(data?.message || "查询失败");
}
return data.data.records || [];
}
export async function loaddatabysqlApi(sql) {
const { data } = await axios.get("/selectlistbysql", {
params: {
sql,
},
});
if (!data?.success) {
throw new Error(data?.message || "查询失败");
}
return data.data.records || [];
}
export async function saveobjtApi(rows) {
const { data } = await axios.post("/saveobjectV2", rows, {
headers: { "Content-Type": "application/json" },
});
if (!data?.success) {
throw new Error(data?.message || "保存失败");
}
return;
}
export default {
getDataBySql,
getDataBySqlPage,
getMultipleDataBySql,
};
// 导出单例
+677
View File
@@ -0,0 +1,677 @@
import { ElMessage } from "element-plus";
import { nextTick, ref, unref } from "vue";
import axios from "@/utils/axios";
import { getDataBySql } from "@/utils/doSql";
const loading = ref(false);
const saving = ref(false);
const error = ref(null);
// export async function getTablePage({
// tableName,
// whereClause = '',
// page = 1,
// size = 20,
// pkColumn = 'b_id desc',
// moduleId = '',
// }) {
// try {
// const { data } = await axios.get('/pagtable', {
// params: {
// tableName,
// pkColumn,
// page,
// size,
// whereClause,
// moduleId,
// },
// })
// // 接口业务失败
// if (!data?.success) {
// return {
// success: false,
// records: [],
// total: 0,
// message: data?.message || '返回数据格式异常',
// }
// }
// const result = data?.data || {}
// return {
// success: data?.success === true,
// records: result.records || [],
// total: result.total || 0,
// message: data?.message || '',
// raw: data,
// }
// }
// catch (err) {
// console.error('getTablePage Error:', err)
// return {
// success: false,
// records: [],
// total: 0,
// message: err?.message || '加载失败',
// }
// }
// }
/**
* 获取分页表格数据
*/
export async function getTablePage({
tableName,
whereClause = "",
page = 1,
size = 20,
pkColumn = "b_id desc",
moduleId = "",
targetRef = null,
paginationTarget = null,
errorRef = null,
} = {}) {
// ✅ 统一重置状态
const resetState = (msg = "") => {
if (targetRef) targetRef.value = [];
if (paginationTarget) paginationTarget.total = 0;
if (errorRef) errorRef.value = msg;
};
try {
if (!tableName) {
throw new Error("tableName 不能为空");
}
const { data } = await axios.get("/pagtable", {
params: {
tableName,
pkColumn,
page,
size,
whereClause,
moduleId,
},
});
// ✅ 后端失败
if (!data?.success) {
const msg = data?.message || "获取数据失败";
resetState(msg);
return {
success: false,
records: [],
total: 0,
message: msg,
};
}
const { records = [], total = 0 } = data.data || {};
// ✅ 正常赋值
if (targetRef) targetRef.value = records;
if (paginationTarget) paginationTarget.total = total;
if (errorRef) errorRef.value = null;
return {
success: true,
records,
total,
message: data.message || "ok",
raw: data,
};
} catch (err) {
console.error("[getTablePage Error]", err);
resetState(err.message || "请求失败");
return {
success: false,
records: [],
total: 0,
message: err.message || "请求失败",
};
}
}
// 获取表格列配置
export async function getTableColumns(tableName) {
try {
loading.value = true;
error.value = "";
const response = await axios.get("/columns", {
params: { tableName },
});
if (response.data?.success) {
return {
success: true,
data: response.data.data,
message: "列配置加载成功",
};
}
throw new Error(response.data?.message || "返回数据格式异常");
} catch (err) {
error.value = `加载列配置失败: ${err.message}`;
console.error("加载列配置错误:", err);
return {
success: false,
data: [],
error: err.message,
};
} finally {
loading.value = false;
}
}
export async function getColumnListConfig(tableName, targetRef) {
try {
// loaddata 接口
loading.value = true;
error.value = null;
const response = await axios.get("/columns", {
params: { tableName },
});
if (response.data?.success) {
targetRef.value = response.data.data || [];
} else {
throw new Error(response.data?.message || "返回数据格式异常");
}
} catch (err) {
error.value = `加载列配置失败: ${err.message}`;
console.error("加载列配置错误:", err);
targetRef.value = [];
} finally {
loading.value = false;
}
}
export async function setColumnListConfig(tableRefOrInstance, tableName) {
await nextTick();
// 关键:同时兼容 ref / 实例
const table = unref(tableRefOrInstance);
if (!table || typeof table.getColumns !== "function") {
ElMessage.warning("表格尚未初始化完成");
return;
}
const columns = table.getColumns();
if (!columns || columns.length === 0) {
ElMessage.warning("未获取到栏位信息");
return;
}
try {
saving.value = true;
error.value = "";
const newColumns = columns
.filter((item) => item.field !== undefined)
.map((item) => ({
prop: item.field,
width: item.renderWidth,
col_TableName: tableName, // 或者你要的默认值
}));
const response = await axios.post("/saveColumn", newColumns, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success("保存成功");
} else {
throw new Error(response.data?.message || "保存失败");
}
} catch (err) {
error.value = `保存失败: ${err.message}`;
ElMessage.error("保存失败");
} finally {
saving.value = false;
}
}
export async function getColumnconfig(tableName, targetRef) {
try {
loading.value = true;
error.value = null;
const response = await axios.get("/columnsEdit", {
params: { tableName },
});
if (response.data?.success) {
// 直接对传入的响应式引用赋值
targetRef.value = response.data.data;
} else {
throw new Error(response.data?.message || "返回数据格式异常");
}
} catch (err) {
error.value = `加载列配置失败: ${err.message}`;
// 出错时赋空数组
targetRef.value = [];
} finally {
loading.value = false;
}
}
export async function getSelectColumns(tableName, targetRef) {
try {
loading.value = true;
error.value = null;
const response = await axios.get("/columnsSelect", {
params: { tableName },
});
if (response.data?.success) {
targetRef.value = response.data.data;
} else {
throw new Error(response.data?.message || "返回数据格式异常");
}
} catch (err) {
error.value = `加载列配置失败: ${err.message}`;
targetRef.value = [];
} finally {
loading.value = false;
}
}
export async function getTableColumnsEditDataset(tableName) {
try {
loading.value = true;
error.value = "";
const response = await axios.get("/columnsEdit", {
params: { tableName },
});
if (response.data?.success) {
return {
success: true,
data: response.data.data,
message: "列配置加载成功",
};
}
throw new Error(response.data?.message || "返回数据格式异常");
} catch (err) {
error.value = `加载列配置失败: ${err.message}`;
console.error("加载列配置错误:", err);
return {
success: false,
data: [],
error: err.message,
};
} finally {
loading.value = false;
}
}
export async function getTableBatchEditColumns(tableName) {
try {
loading.value = true;
error.value = "";
const response = await axios.get("/columnsBatchEdit", {
params: { tableName },
});
if (response.data?.success) {
return {
success: true,
data: response.data.data,
message: "列配置加载成功",
};
}
throw new Error(response.data?.message || "返回数据格式异常");
} catch (err) {
error.value = `加载列配置失败: ${err.message}`;
console.error("加载列配置错误:", err);
return {
success: false,
data: [],
error: err.message,
};
} finally {
loading.value = false;
}
}
export async function getMainTable(
tableName,
targetRef,
originalRef,
pkColumnValue,
pkColumn = "b_id",
) {
try {
loading.value = true;
error.value = "";
targetRef.value = {};
const params = {
tableName: tableName || "v_bs_business",
pkColumn,
pkColumnValue,
};
const response = await axios.get("/getobject", { params });
if (response.data?.success) {
const row = response.data.data || {};
if (Object.keys(row).length === 0) {
ElMessage.warning("未找到对应记录");
}
targetRef.value = { ...row };
originalRef.value = { ...row };
} else {
throw new Error(response.data?.message || "返回数据格式异常");
}
} catch (err) {
error.value = `加载客户详情失败: ${err.message}`;
console.error("加载客户详情错误:", err);
} finally {
loading.value = false;
}
}
export async function getMainTableDataset(tableName, pkColumnValue) {
try {
loading.value = true;
error.value = "";
const params = {
tableName,
pkColumn: "b_id",
pkColumnValue,
};
const response = await axios.get("/getobject", { params });
if (response.data?.success) {
return {
success: true,
data: {
current: { ...response.data.data },
original: { ...response.data.data },
},
message: "主表数据加载成功",
};
}
throw new Error(response.data?.message || "返回数据格式异常");
} catch (err) {
error.value = `加载主表数据失败: ${err.message}`;
console.error("加载主表错误:", err);
return {
success: false,
data: {
current: null,
original: null,
},
error: err.message,
};
} finally {
loading.value = false;
}
}
export async function getSubTable(tableName, targetRef, originalRef, whereClause) {
try {
loading.value = true;
error.value = null;
const params = {
tableName: tableName || "v_bs_business_container",
pkColumn: "subid",
whereClause,
};
const response = await axios.get("/pagdetail", { params });
if (response.data?.success) {
const rawData = JSON.parse(JSON.stringify(response.data.data.records || []));
targetRef.value = JSON.parse(JSON.stringify(rawData));
originalRef.value = JSON.parse(JSON.stringify(rawData));
} else {
throw new Error(response.data?.message || "返回数据格式异常");
}
} catch (err) {
error.value = `加载数据失败: ${err.message}`;
console.error("API Error:", err);
return { data: [], total: 0 };
} finally {
loading.value = false;
}
}
// setColumnListConfig
export async function handleSaveColumn(tableRefOrInstance, tableName) {
await nextTick();
// 关键:同时兼容 ref / 实例
const table = unref(tableRefOrInstance);
if (!table || typeof table.getColumns !== "function") {
ElMessage.warning("表格尚未初始化完成");
return;
}
const columns = table.getColumns();
if (!columns || columns.length === 0) {
ElMessage.warning("未获取到栏位信息");
return;
}
try {
saving.value = true;
error.value = "";
const newColumns = columns
.filter((item) => item.field !== undefined)
.map((item) => ({
prop: item.field,
width: item.renderWidth,
col_TableName: tableName, // 或者你要的默认值
}));
const response = await axios.post("/saveColumn", newColumns, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success("保存成功");
} else {
throw new Error(response.data?.message || "保存失败");
}
} catch (err) {
error.value = `保存失败: ${err.message}`;
ElMessage.error("保存失败");
} finally {
saving.value = false;
}
}
export async function handleSaveColumnEdit(tableRefOrInstance, tableName) {
await nextTick();
// 关键:同时兼容 ref / 实例
const table = unref(tableRefOrInstance);
if (!table || typeof table.getColumns !== "function") {
ElMessage.warning("表格尚未初始化完成");
return;
}
const columns = table.getColumns();
if (!columns || columns.length === 0) {
ElMessage.warning("未获取到栏位信息");
return;
}
try {
saving.value = true;
error.value = "";
const newColumns = columns
.filter((item) => item.field !== undefined)
.map((item) => ({
prop: item.field,
edit_width: item.renderWidth,
col_TableName: tableName, // 或者你要的默认值
}));
// console.log(newColumns)
const response = await axios.post("/saveColumnEdit", newColumns, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success("保存成功");
} else {
throw new Error(response.data?.message || "保存失败");
}
} catch (err) {
error.value = `保存失败: ${err.message}`;
ElMessage.error("保存失败");
} finally {
saving.value = false;
}
}
export async function getTableLabelEdit(tableName) {
const res = await getDataBySql(
`select * from s_columnLib WHERE col_TableName = '${tableName}' and col_edit='1' order by col_edit_position`,
);
const map = Object.create(null);
const records = res?.records || [];
records.forEach((col) => {
if (col?.col_FieldName) {
map[col.col_FieldName] = {
caption: col.col_Caption || "",
required: col.col_AllowNull !== "1",
};
}
});
return map;
}
// export async function handleMappingConfig(selectId, whereDataset, whereClause, targetRef, originalRef) {
// let selectSql = ''
// let selectOrder = ''
// const data_selectsql = await getDataBySql(`select * from s_selectsql where selectid='${selectId}' `)
// if (data_selectsql.records.length > 0) {
// selectSql = data_selectsql.records[0].selectsql
// selectOrder = data_selectsql.records[0].orderfield
// }
// whereDataset = unref(whereDataset)
// for (const key in whereDataset) {
// const v = (whereDataset[key] ?? '').toString().trim()
// const regex = new RegExp(
// `\\s+and\\s+[^\\s]+\\s*(=|like)\\s*:${escapeRegExp(key)}\\b`,
// 'gi',
// )
// if (v === '') {
// selectSql = selectSql.replace(regex, '')
// }
// else {
// selectSql = selectSql.replaceAll(
// `:${key}`,
// `'${v.replaceAll('\'', '\'\'')}'`,
// )
// }
// }
// let sql = `${selectSql} ${whereClause} order by ${selectOrder}`
// console.log('sql=----------')
// console.log(sql)
// const data = await getDataBySql(sql)
// if (data.records.length == 0) {
// ElMessage.warning('数据不存在!')
// return
// }
// const data_selectdatamap = await getDataBySql(`select * from s_selectdatamap where selectid='${selectId}' `)
// if (data_selectdatamap.records.length > 0) {
// const maps = data_selectdatamap?.records || []
// const resultList = data.records.map((row) => {
// const obj = {}
// maps.forEach((item) => {
// obj[item.des_field] = row[item.source_field]
// })
// return obj
// })
// // console.log('resultList=-=-=-=-=-')
// // console.log(resultList)
// // subtable.value = resultList
// // subtableoriginal.value = resultList
// targetRef.value = [
// ...targetRef.value,
// ...JSON.parse(JSON.stringify(resultList)),
// ]
// }
// }
export async function handleMappingConfig(selectId, subidliststr, targetRef) {
let selectSql = "";
let selectOrder = "";
const data_selectsql = await getDataBySql(
`select * from s_selectsql where selectid='${selectId}' `,
);
if (data_selectsql.records.length > 0) {
selectSql = data_selectsql.records[0].selectsql;
}
let sql = `${selectSql} ${subidliststr} `;
console.log("sql=----------");
console.log(sql);
const data = await getDataBySql(sql);
if (data.records.length == 0) {
return;
}
const data_selectdatamap = await getDataBySql(
`select * from s_selectdatamap where selectid='${selectId}' `,
);
if (data_selectdatamap.records.length > 0) {
const maps = data_selectdatamap?.records || [];
const resultList = data.records.map((row) => {
const obj = {};
maps.forEach((item) => {
obj[item.des_field] = row[item.source_field];
});
return obj;
});
targetRef.value = [...targetRef.value, ...JSON.parse(JSON.stringify(resultList))];
}
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
export const editableStates = ["草拟", "审核驳回", "送审"];
// 是否可编辑
export function isEditableState(state) {
return editableStates.includes(state || "");
}
// 审核通过状态
export const approvedStates = ["审核通过"];
// 是否审核通过
export function isApprovedState(state) {
return !approvedStates.includes(state || "");
}
export default {
getTablePage,
getTableColumns,
getColumnListConfig,
setColumnListConfig,
getColumnconfig,
getSelectColumns,
getTableColumnsEditDataset,
getTableBatchEditColumns,
getMainTable,
getMainTableDataset,
getSubTable,
handleSaveColumn,
handleSaveColumnEdit,
getTableLabelEdit,
handleMappingConfig,
isEditableState,
isApprovedState,
};
// 导出单例
+342
View File
@@ -0,0 +1,342 @@
import ExcelJS from "exceljs";
import { VxeUI } from "vxe-pc-ui";
import { formatValueByFullFormat, getAlignByFormat, updateTableFooterData } from "@/utils/column";
export function ExportVxeTableExcel(tableRef) {
if (!tableRef) {
return;
}
// columns
const fullColumns = tableRef.getFullColumns();
const exportColumns = buildColumns(fullColumns) || [];
const exportDatas =
tableRef.getCheckboxRecords().length > 0
? tableRef.getCheckboxRecords()
: tableRef.getFullData();
const footerDatas = updateTableFooterData(exportColumns, exportDatas);
ExportExcel(exportColumns, exportDatas, footerDatas);
}
export function ExportExcel(columns, data, footerData = []) {
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet("Sheet1");
const exportColumns = columns;
const exportDatas = data;
if (!exportColumns || exportColumns.length === 0) {
console.error("没有找到列数据");
return;
}
VxeUI.modal.message({
content: `正在导出中 . . .`,
status: "loading",
duration: -1,
});
// 1. 提取所有叶子节点(数据列)
const leafColumns = [];
const headerData = [];
const mergeInfo = [];
// 2. 解析列结构,构建表头数据
function parseColumns(cols, currentLevel = 0, startCol = 0) {
let colIndex = startCol;
cols.forEach((col) => {
// 确保当前层级的数组存在
if (!headerData[currentLevel]) {
headerData[currentLevel] = [];
}
if (col.children && col.children.length > 0) {
// 处理有子级的列
const childLeafCount = parseColumns(col.children, currentLevel + 1, colIndex);
// 记录合并信息(水平合并)
if (childLeafCount > 1) {
mergeInfo.push({
startRow: currentLevel + 1, // ExcelJS 行号从1开始
startCol: colIndex + 1, // ExcelJS 列号从1开始
endRow: currentLevel + 1,
endCol: colIndex + childLeafCount,
});
}
// 设置当前行的表头
headerData[currentLevel][colIndex] = col.label || "";
// 填充子级占据的列
for (let i = 1; i < childLeafCount; i++) {
headerData[currentLevel][colIndex + i] = "";
}
colIndex += childLeafCount;
} else {
// 处理叶子节点
leafColumns.push(col);
// 设置当前行和所有下层的表头
for (let level = currentLevel; level < headerData.length; level++) {
if (!headerData[level]) headerData[level] = [];
if (level === currentLevel) {
headerData[level][colIndex] = col.label || "";
} else {
headerData[level][colIndex] = col.label || ""; // 对于垂直合并,需要填充标签
}
}
// 记录垂直合并信息
if (headerData.length - currentLevel > 1) {
mergeInfo.push({
startRow: currentLevel + 1,
startCol: colIndex + 1,
endRow: headerData.length, // 合并到最后一行
endCol: colIndex + 1,
});
}
colIndex++;
}
});
return colIndex - startCol; // 返回该组列占用的列数
}
// 初始化 headerData 数组
const maxDepth = getMaxDepth(exportColumns);
for (let i = 0; i < maxDepth; i++) {
headerData[i] = [];
}
parseColumns(exportColumns);
// 3. 添加表头行
headerData.forEach((rowData, rowIndex) => {
const row = sheet.addRow(rowData);
// 设置表头样式
row.eachCell((cell, colNumber) => {
cell.style = {
font: { bold: true, size: 10 },
alignment: { horizontal: "center", vertical: "middle" },
fill: {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFE0E0E0" },
},
border: {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" },
},
};
});
});
// 4. 合并单元格(先排序,避免重复合并)
// 按照从左上到右下的顺序排序
mergeInfo.sort((a, b) => {
if (a.startRow !== b.startRow) return a.startRow - b.startRow;
return a.startCol - b.startCol;
});
// 记录已合并的单元格
const mergedCells = new Set();
mergeInfo.forEach((merge) => {
// 检查这个合并区域是否与已合并的区域冲突
let canMerge = true;
for (let r = merge.startRow; r <= merge.endRow; r++) {
for (let c = merge.startCol; c <= merge.endCol; c++) {
const cellKey = `${r}-${c}`;
if (mergedCells.has(cellKey)) {
canMerge = false;
break;
}
}
if (!canMerge) break;
}
if (canMerge) {
try {
sheet.mergeCells(merge.startRow, merge.startCol, merge.endRow, merge.endCol);
// 记录已合并的单元格
for (let r = merge.startRow; r <= merge.endRow; r++) {
for (let c = merge.startCol; c <= merge.endCol; c++) {
mergedCells.add(`${r}-${c}`);
}
}
} catch (error) {
console.warn(
`无法合并单元格 ${merge.startRow}:${merge.startCol}-${merge.endRow}:${merge.endCol}`,
error,
);
}
}
});
// 5. 设置列宽
sheet.columns = leafColumns.map((col) => ({
key: col.prop,
width: pxToExcelWidth(col.width) || 20,
}));
// 6. 填充数据
exportDatas.forEach((item, rowIndex) => {
const rowData = {};
leafColumns.forEach((col, colIndex) => {
let value = item[col.prop];
// 如果是空值,设置为空字符串
rowData[col.prop] = formatValueByFullFormat(value, col.displayFormat);
});
const row = sheet.addRow(rowData);
// 设置数据行样式
row.eachCell((cell, colNumber) => {
const leafCol = leafColumns[colNumber - 1];
const style = {
font: { size: 10 },
alignment: { vertical: "middle" },
border: {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" },
},
};
style.alignment.horizontal = getAlignByFormat(leafCol.displayFormat);
cell.style = style;
});
});
// 7. 添加汇总行
if (footerData.length > 0) {
footerData.forEach((footerItem, rowIndex) => {
const summaryRowData = {};
// 遍历所有列,计算每个列的汇总值
leafColumns.forEach((col) => {
summaryRowData[col.prop] = formatValueByFullFormat(footerItem[col.prop], col.displayFormat);
});
// 添加汇总行
const summaryRow = sheet.addRow(summaryRowData);
summaryRow.eachCell((cell) => {
// 设置单元格样式
const style = {
font: { bold: true, size: 10 },
alignment: { vertical: "middle" },
fill: {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFF0F0F0" },
},
border: {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" },
},
};
style.alignment.horizontal = "right";
// 应用样式
cell.style = style;
});
});
}
// 8. 保存文件
workbook.xlsx
.writeBuffer()
.then((buffer) => {
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `export-${new Date().getTime()}.xlsx`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
VxeUI.modal.close();
VxeUI.modal.message({
content: `导出成功`,
status: "success",
});
})
.catch((err) => {
console.error("生成 Excel 文件失败:", err);
VxeUI.modal.message({
content: `导出失败`,
status: "error",
});
});
}
// 获取最大深度
function getMaxDepth(columns, currentDepth = 1) {
let maxDepth = currentDepth;
columns.forEach((col) => {
if (col.children && col.children.length > 0) {
const childDepth = getMaxDepth(col.children, currentDepth + 1);
maxDepth = Math.max(maxDepth, childDepth);
}
});
return maxDepth;
}
function pxToExcelWidth(px) {
return Number(px) / 8;
}
function buildColumns(arr) {
return arr
.filter((item) => {
if (
(item.type === "checkbox" || item.type === "seq") &&
(!item.children || item.children.length === 0)
) {
return false;
} else if (item.title === "操作") {
return false;
}
return true;
})
.map((item) => {
let column = {};
if (item.params) {
column = { ...item.params };
}
column.label = item.title;
column.width = item.renderWidth || item.width;
if (item.children && item.children.length > 0) {
column.children = buildColumns(item.children);
}
return column;
});
}
+379
View File
@@ -0,0 +1,379 @@
import { ElMessage } from "element-plus";
import { saveAs } from "file-saver";
import JSZip from "jszip";
import {
closeUploadProgress,
showUploadProgress,
updateUploadProgress,
} from "@/components/upload-progress/index";
import axios from "@/utils/axios";
const AliyunBucket = "g3hy";
export function getAliyunKeyApi() {
return axios.get("/file/getAliyunKey", {
params: {
bucket: AliyunBucket,
},
});
}
export function insertFileApi(data) {
return axios.post("/file/insert", data);
}
export function deleteFileApi(tableName, id) {
return axios.post("/file/delete", {
tableName,
bucketName: AliyunBucket,
id,
});
}
/**
* 选择并压缩图片
*/
export function uploadImage(options = {}) {
const { quality = 0.9, maxSize = 512000 } = options;
return new Promise((resolve, reject) => {
if (typeof document === "undefined") {
reject(new Error("Require browser environment"));
return;
}
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.style.display = "none";
input.onchange = async (e) => {
const file = e.target.files?.[0];
if (!file) {
reject(new Error("No file selected"));
return;
}
try {
if (!file.type.startsWith("image/")) {
throw new Error("Invalid image file");
}
// 创建一个临时URL来加载图片
const url = URL.createObjectURL(file);
// 调用 compressImage 进行压缩
const compressResult = await compressImage(url, quality, maxSize);
// 释放临时URL
URL.revokeObjectURL(url);
resolve({
blob: compressResult.blob,
url: compressResult.url,
fileName: file.name,
fileExt: getFileExtension(file.name),
fileSize: file.size,
});
} catch (err) {
reject(err);
} finally {
document.body.removeChild(input);
}
};
document.body.appendChild(input);
input.click();
});
}
/**
* 选择视频
*/
export function uploadVideo() {
return new Promise((resolve, reject) => {
if (typeof document === "undefined") {
reject(new Error("Require browser environment"));
return;
}
const input = document.createElement("input");
input.type = "file";
input.accept = "video/*";
input.style.display = "none";
input.onchange = (e) => {
const file = e.target.files?.[0];
if (!file) {
reject(new Error("No file selected"));
return;
}
if (!file.type.startsWith("video/")) {
reject(new Error("Invalid video file"));
return;
}
resolve({
blob: file,
url: URL.createObjectURL(file),
fileName: file.name,
fileExt: getFileExtension(file.name),
fileSize: file.size,
});
document.body.removeChild(input);
};
document.body.appendChild(input);
input.click();
});
}
// 压缩图片
export async function compressImage(url, quality = 0.9, maxSize = 512000) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = url;
img.onload = async () => {
try {
// 获取原图大小
const imgSize = await fetch(url)
.then((res) => res.blob())
.then((blob) => blob.size);
if (imgSize <= maxSize) {
return resolve({ url, size: imgSize, blob: await fetch(url).then((res) => res.blob()) });
}
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) return reject(new Error("无法获取canvas上下文"));
let targetWidth = img.width;
let targetHeight = img.height;
// 最大边长限制
const maxEdge = 2000;
if (targetWidth > maxEdge || targetHeight > maxEdge) {
if (targetWidth > targetHeight) {
targetHeight = (maxEdge * targetHeight) / targetWidth;
targetWidth = maxEdge;
} else {
targetWidth = (maxEdge * targetWidth) / targetHeight;
targetHeight = maxEdge;
}
}
canvas.width = targetWidth;
canvas.height = targetHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
const getBlob = (q) =>
new Promise((resolve, reject) => {
canvas.toBlob(
(blob) => (blob ? resolve(blob) : reject(new Error("Blob转换失败"))),
"image/jpeg",
q,
);
});
let blob = await getBlob(quality);
// 调整质量直到接近最大文件大小
let minQ = 0.5;
let maxQ = quality;
let attempts = 0;
while (blob.size > maxSize && maxQ - minQ > 0.01 && attempts < 5) {
const midQ = (minQ + maxQ) / 2;
blob = await getBlob(midQ);
if (blob.size > maxSize) {
maxQ = midQ;
} else {
minQ = midQ;
}
attempts++;
}
resolve({ blob, size: blob.size, url: URL.createObjectURL(blob) });
} catch (err) {
reject(err);
}
};
img.onerror = () => reject(new Error("图片加载失败"));
});
}
export function formatFileSize(size) {
if (size < 1024) return `${size} B`;
else if (size < 1024 * 1024) return `${(size / 1024).toFixed(2)} KB`;
else if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(2)} MB`;
else return `${(size / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}
// 获取文件扩展名并进行分类
export function getFileExtension(fileName) {
// 获取文件扩展名
const fileExt = fileName.includes(".")
? fileName.slice(fileName.lastIndexOf(".") + 1).toLowerCase() // 获取扩展名并转为小写
: "";
// 如果是office类文件,将其归类到 "office"
if (["docx", "docm", "xls", "xlsx"].includes(fileExt)) {
return "office";
}
// 返回扩展名
return fileExt;
}
// 上传到阿里云
// blob fileName fileSize xh moduleid father cateid
export function uploadFile(blob, fileName, fileExt, fileSize, xh, moduleid, father, cateid) {
return new Promise((resolve, reject) => {
getAliyunKeyApi()
.then(async (res) => {
try {
const aliyunKey = res.data.data;
const formData = new FormData();
let uniqueName = `${Date.now()}_${Math.random().toString(36).slice(2)}`;
if (fileExt) {
uniqueName += `.${fileExt}`;
}
formData.append("key", `${aliyunKey.dir}${uniqueName}`);
formData.append("OSSAccessKeyId", aliyunKey.accessid);
formData.append("policy", aliyunKey.policy);
formData.append("Signature", aliyunKey.signature);
formData.append("file", blob, uniqueName);
const xhr = new XMLHttpRequest();
xhr.open("POST", aliyunKey.host, true);
xhr.onload = async () => {
if (xhr.status === 200 || xhr.status === 204) {
const allFilePath = `${aliyunKey.host}/${aliyunKey.dir}${uniqueName}`;
const insertRes = await insertFileApi({
father,
mx_moduleid: moduleid,
mx_xh: xh,
mx_filename: fileName,
mx_mapfilename: allFilePath,
mx_filesize: fileSize,
mx_fileext: fileExt,
mx_cate_id: cateid || "",
});
if (insertRes.data.success) {
resolve({
father,
mx_moduleid: moduleid,
mx_xh: xh,
mx_filename: fileName,
mx_mapfilename: allFilePath,
mx_filesize: fileSize,
mx_fileext: fileExt,
mx_cate_id: cateid || "",
});
} else {
reject(new Error(insertRes.data.message));
}
} else {
ElMessage.error("上传失败,请稍后再试");
reject(new Error(`OSS 上传失败: ${xhr.responseText}`));
}
};
xhr.onerror = () => {
ElMessage.error("上传失败,请稍后再试");
reject(new Error("OSS 上传异常"));
};
xhr.send(formData);
} catch (err) {
ElMessage.error("上传失败,请稍后再试");
reject(err);
}
})
.catch(reject);
});
}
export function getAliyunFileBlob(url) {
return new Promise((resolve, reject) => {
// 创建一个独立的异步函数来处理 fetch 请求
async function fetchFile() {
try {
const response = await fetch(url, { mode: "cors" });
// 检查响应状态
if (!response.ok) {
ElMessage.error(`下载失败:${response.status}`);
reject(new Error(`下载失败:${response.status}`));
return;
}
// 获取文件内容
const blob = await response.blob();
// 返回blob
resolve(blob);
} catch (error) {
// 捕获网络请求失败或其他错误
ElMessage.error("下载文件时发生错误");
reject(error);
}
}
// 调用异步函数
fetchFile();
});
}
export function downloadZip(files, fileName = "文件合集", callback) {
const zip = new JSZip();
const addedFiles = new Set(); // 用来存储已添加的文件名
let result = files.map((item) => {
let promise = getAliyunFileBlob(item.mx_mapfilename).then((res) => {
// 处理文件名重复的情况
let uniqueFileName = getUniqueFileName(item.mx_filename, addedFiles);
zip.file(uniqueFileName, res, { binary: true });
addedFiles.add(uniqueFileName); // 添加文件名到已添加的集合中
});
return promise;
});
Promise.all(result)
.then(() => {
zip.generateAsync({ type: "blob" }).then((res) => {
callback && callback();
saveAs(res, `${fileName}.zip`);
});
})
.catch(() => {
callback && callback();
});
}
// 处理文件名重复的函数
function getUniqueFileName(fileName, addedFiles) {
let uniqueFileName = fileName;
let counter = 1;
// 如果文件名已经存在,则在文件名后加上 (1), (2), ...
while (addedFiles.has(uniqueFileName)) {
const fileNameWithoutExtension = uniqueFileName.replace(/\.[^/.]+$/, "");
const extension = uniqueFileName.slice(uniqueFileName.lastIndexOf("."));
uniqueFileName = `${fileNameWithoutExtension} (${counter})${extension}`;
counter++;
}
return uniqueFileName;
}
+140
View File
@@ -0,0 +1,140 @@
import { ElMessage } from "element-plus";
import { ref, unref } from "vue";
import axios from "@/utils/axios";
import { useUserStore } from "@/store/user";
const userStore = useUserStore();
const loading = ref(false);
const saving = ref(false);
const error = ref(null);
export async function getDefaultValue(targetRef, tablename) {
try {
loading.value = true;
error.value = null;
const params = {
tableName: tablename || "v_bs_business",
};
const response = await axios.get("/getDefaultvalue", { params });
if (response.data?.success) {
//targetRef.value = JSON.parse(JSON.stringify(response.data.data || []));
console.log("response.data.data=-=-=-");
console.log(response.data.data);
Object.assign(targetRef.value, response.data.data);
} else {
throw new Error(response.data?.message || "返回数据格式异常");
}
} catch (err) {
error.value = `加载数据失败: ${err.message}`;
console.error("API Error:", err);
return { data: [], total: 0 };
} finally {
loading.value = false;
}
}
export async function checkMustField(targetObj, tablename) {
const targetdata = unref(targetObj);
const params = {
tableName: tablename || "v_bs_business",
};
const response = await axios.get("/getMustfield", { params });
if (!response.data?.success) {
throw new Error(response.data?.message || "返回数据格式异常");
}
const mustFields = response.data.data || [];
for (const field of mustFields) {
const fieldName = field.col_FieldName;
const value = targetdata[fieldName];
console.log(value);
if (value === null || value === undefined || String(value).trim() === "") {
const caption = field.col_Caption || fieldName;
ElMessage.error(`${caption} 不能为空`);
return false;
}
}
return true;
}
export async function getMoneyExRate({
rows = [],
field = "v_mx_moneytype_id",
defaultMoney = "RMB",
tableName = "b_money",
moneyField = "b_name",
rateField = "b_hl",
} = {}) {
let exrate = 1;
if (!Array.isArray(rows) || rows.length === 0) {
return exrate;
}
const row = rows.find((item) => item[field] && item[field] !== defaultMoney);
if (!row) {
return exrate;
}
const moneyid = row[field];
const sql = `
select ${rateField}
from ${tableName}
where ${moneyField}='${moneyid}'
`;
const res = await getDataBySql(sql);
if (res?.records?.length > 0) {
exrate = Number(res.records[0][rateField]) || 1;
}
return exrate;
}
export function toMoney(val, scale = 3) {
const num = Number(val || 0);
if (isNaN(num)) {
return 0;
}
return Number(num.toFixed(scale));
}
export const editableStates = ["草拟", "审核驳回", "送审"];
export function isEditableStateAndQR(state, cwqr) {
return editableStates.includes(state || "") && String(cwqr) !== "1";
}
export function isNotEmpty(val) {
return val !== null && val !== undefined && String(val).trim() !== "";
}
export function getFieldString(arr, field, limit = 100) {
return [...new Set(arr.map((item) => item[field]).filter(Boolean))].slice(0, limit).join(",");
}
export function morePermission(permissionId) {
const userStore = useUserStore();
return !userStore.hasActionPermission(permissionId);
}
// 默认导出所有方法
export default {
getDefaultValue,
checkMustField,
getMoneyExRate,
toMoney,
isEditableStateAndQR,
isNotEmpty,
getFieldString,
};
@@ -0,0 +1,40 @@
export function processGridKeydown(event, $table) {
if (!$table) {
return;
}
const currentRow = $table.getCurrentRecord();
const currentRowIndex = $table.getRowIndex(currentRow);
const currentColumn = $table.getCurrentColumn();
const { key } = event;
// 获取表格的所有数据和列配置
const fullData = $table.getFullData();
const columns = $table.getColumns();
const currentColumnIndex = $table.getColumnIndex(currentColumn);
// ArrowUp - 向上移动一行
if (key === "ArrowUp" && currentRowIndex > 0) {
const toIndex = currentRowIndex - 1;
$table.setEditRow(fullData[toIndex], currentColumn.property);
$table.setCurrentRow(fullData[toIndex]);
}
// ArrowDown - 向下移动一行
else if (key === "ArrowDown" && currentRowIndex < fullData.length - 1) {
const toIndex = currentRowIndex + 1;
$table.setEditRow(fullData[toIndex], currentColumn.property);
$table.setCurrentRow(fullData[toIndex]);
}
// ArrowLeft - 向左移动一列
else if (key === "ArrowLeft" && currentColumnIndex > 0) {
const toIndex = currentColumnIndex - 1;
$table.setEditCell(currentRow, columns[toIndex]);
$table.setCurrentColumn(columns[toIndex]);
}
// ArrowRight - 向右移动一列
else if (key === "ArrowRight" && currentColumnIndex < columns.length - 1) {
const toIndex = currentColumnIndex + 1;
$table.setEditCell(currentRow, columns[toIndex]);
$table.setCurrentColumn(columns[toIndex]);
}
}
+980
View File
@@ -0,0 +1,980 @@
// import ssDialog from '@/components/ssDialog.vue'
// import shDialog from '@/components/shDialog.vue'
// import bhDialog from '@/components/bhDialog.vue'
import toshDialog from "@/components/sendsh/toshDialog.vue";
import { createVNode, render, h } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import axios from "@/utils/axios";
import { getDataBySql } from "@/utils/doSql";
import dayjs from "dayjs";
import { useUserStore } from "@/store/user";
/**
* 审核中心 - Vue3版本
* @param {String} ywid - 业务ID
* @param {String} savetable - 主表表名
* @param {String} ModuleID - 模块ID
* @param {String} state - 审核状态(如 审核通过、驳回)
* @param {Array} shsetdatas - 审核流程设定
* @param {Function} func - 回调函数
*/
export async function mod_shcenter_sh(ywid, savetable, ModuleID, state, shsetdatas, Fuserid, func) {
try {
// === 1. 读取业务主表数据 ===
const mainSql = `select * from ${savetable} where b_id='${ywid}' order by b_id`;
const mainRes = await axios.get("/selectlistbysql", { params: { sql: mainSql } });
const ywdatas = mainRes.data?.data?.records || [];
if (ywdatas.length === 0) {
ElMessage.warning("当前业务单不存在");
return;
}
const rowdata = ywdatas[0];
const ywownerid = rowdata.b_owner_id;
// === 2. 查询待审记录 ===
const shSql = `select * from sh_record where b_dj_id='${ywid}' and b_module_id='${ModuleID}' order by b_id desc`;
const shRes = await axios.get("/selectlistbysql", { params: { sql: shSql } });
const shrecords = shRes.data?.data?.records || [];
if (shrecords.length === 0) {
ElMessage.warning("没有待审记录");
return;
}
const now_level = Number(shrecords[0].b_level);
const shlevel = now_level + 1;
const dsh_useid = shrecords[0].b_tosh_useid;
const userid = Fuserid; // 当前登录用户ID(请根据实际修改)
if (String(dsh_useid).trim() !== String(userid).trim()) {
ElMessage.error("你没有该票业务的审批权限");
return;
}
// === 3. 判断审批权限、是否有下一步 ===
let can_sh = false;
let can_nextsh = false;
let level_userid = [];
for (const shset of shsetdatas) {
if (Number(shset.b_level) === Number(shlevel)) {
if (shset.b_user_id === dsh_useid) {
can_sh = true;
}
}
if (Number(shset.b_level) === Number(shlevel + 1)) {
can_nextsh = true;
level_userid.push(shset.b_user_id);
}
}
if (!can_sh) {
ElMessage.error("你没有该票业务的审批权限");
return;
}
// === 4. 设置当前状态 ===
const xxx = ["一", "二", "三", "四", "五", "六", "七", "八"];
let sh_state = can_nextsh ? `${xxx[now_level]}审通过` : "审核通过";
const str_leveluserid = level_userid.map((item) => `'${item}'`).join(",");
const searchcondition = `b_id in (${str_leveluserid})`;
const shSql1 = `select * from b_user where ${searchcondition}`;
const shRes1 = await axios.get("/selectlistbysql", { params: { sql: shSql1 } });
const shrecords_id = shRes1.data?.data?.records || [];
const level_userid_upadte = shrecords_id.map((item) => ({
b_id: item.b_id,
b_name: item.b_name,
}));
// === 5. 弹出选择审批人弹窗 ===
const data = await openShDialog(level_userid_upadte, ywownerid);
if (!data) {
ElMessage.info("已取消转送");
return;
}
// === 7. 调用保存函数 ===
await save_sh_data(
ModuleID,
userid,
savetable,
rowdata,
sh_state,
shlevel,
data.user,
data.memo,
func,
);
} catch (err) {
console.error(err);
ElMessage.error("审批失败");
}
}
// export function openShDialog(levelUserIds, ywownerid) {
// return new Promise(resolve => {
// const container = document.createElement('div')
// document.body.appendChild(container)
// const closeDialog = () => {
// render(null, container)
// document.body.removeChild(container)
// }
// const vnode = createVNode(shDialog, {
// modelValue: true,
// levelUserIds,
// ywownerid,
// onConfirm: data => {
// closeDialog()
// resolve(data)
// },
// onCancel: () => {
// closeDialog()
// resolve(null)
// }
// })
// render(vnode, container)
// })
// }
/**
* 模拟保存审批数据
*/
async function save_sh_data(
ModuleID,
userid,
savetable,
rowdata,
state,
shlevel,
tosh_useid,
sh_memo,
func,
) {
if (rowdata == undefined) return;
const updateRows = [];
const insert_sh_records = [];
var oldstate = rowdata.b_state;
const inputdatetime = dayjs().format("YYYY-MM-DD HH:mm:ss");
const obj = { b_id: rowdata.b_id, b_state: state };
updateRows.push(obj);
insert_sh_records.push({
b_module_id: ModuleID,
b_user_id: userid,
b_level: shlevel,
b_name: state,
b_dj_id: rowdata.b_id,
b_sh: "1",
b_sh_date: inputdatetime,
b_sh_memo: sh_memo,
b_tosh_useid: tosh_useid,
});
const requestData = [];
if (updateRows.length > 0) {
requestData.push({
tableName: savetable,
keyField: "b_id",
inserts: [],
updates: updateRows,
deletes: [],
});
requestData.push({
tableName: "sh_record",
keyField: "b_id",
inserts: insert_sh_records,
updates: [],
deletes: [],
});
if (savetable === "cw_zdorder") {
const updateFeeData = [];
const zdidarr = updateRows.map((row) => `'${row.b_id}'`);
// 获取账单状态
const getBStateById = (id) => {
const row = updateRows.find((r) => r.b_id === id);
return row ? row.b_state : "";
};
// 这里替代 loaddatas()
const searchsql = `isnull(mx_zdorderid, '') in (${zdidarr.join(",")})`;
const cwres = await axios.get("/selectlistbysql", {
params: {
sql: `select * from bs_business_cw where ${searchsql} order by subid`,
},
});
const cwrows = cwres.data?.data?.records || [];
for (const cw of cwrows) {
const zdstate = getBStateById(cw.mx_zdorderid) || state;
updateFeeData.push({
subid: cw.subid,
father: cw.father,
mx_zdorder_state: zdstate,
});
}
if (updateFeeData.length > 0) {
requestData.push({
tableName: "bs_business_cw",
keyField: "subid",
inserts: [],
updates: updateFeeData,
deletes: [],
});
}
}
}
const response = await axios.post("/saveobjectV2", requestData, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success(state + "成功!");
if (typeof func === "function") {
await func();
}
} else {
throw new Error(response.data?.message || "保存失败");
}
}
export const main_shcenter_ss = async (ywid, savetable, ModuleID, state, userid, func) => {
// 1️⃣ 校验业务单是否存在
let searchsql = `select * from ${savetable} where b_id='${ywid}' order by b_id`;
const ywdatasRes = await axios.get("/selectlistbysql", { params: { sql: searchsql } });
const ywdatas = ywdatasRes.data?.data?.records || [];
if (!ywdatas.length) {
ElMessage.error("当前业务单号不存在");
return false;
}
const newstate = ywdatas[0].b_state;
const canuse_state = ["草拟", "审核驳回", ""];
if (!canuse_state.includes(newstate)) {
ElMessage.warning(`只有 ${canuse_state.join(",")} 的记录才能 ${state}!`);
return false;
}
// 2️⃣ 获取审批配置
let sql = `select * from sh_b_set where b_module_id='${ModuleID}' order by b_level`;
const shsetRes = await axios.get("/selectlistbysql", { params: { sql } });
const shsetdatas = shsetRes.data?.data?.records || [];
const level_userid = shsetdatas.filter((i) => i.b_level === "1").map((i) => i.b_user_id);
if (!level_userid.length) {
ElMessage.error("当前模块未配置一级审批人");
return false;
}
const str_leveluserid = level_userid.map((item) => `'${item}'`).join(",");
const searchcondition = `b_id in (${str_leveluserid})`;
const shSql1 = `select * from b_user where ${searchcondition}`;
const shRes1 = await axios.get("/selectlistbysql", { params: { sql: shSql1 } });
const shrecords_id = shRes1.data?.data?.records || [];
const level_userid_upadte = shrecords_id.map((item) => ({
b_id: item.b_id,
b_name: item.b_name,
}));
// 3️⃣ 打开弹窗
const selected = await openSelectApproverDialog(level_userid_upadte);
if (!selected) {
ElMessage.info("已取消送审");
return;
}
// 4️⃣ 保存送审逻辑
await save_ss_data(
ModuleID,
userid,
savetable,
ywid,
newstate,
state,
selected.user,
selected.memo,
func,
);
};
/**
* 动态创建审批人选择弹窗
*/
// function openSelectApproverDialog(levelUserIds) {
// return new Promise(resolve => {
// const container = document.createElement('div')
// document.body.appendChild(container)
// const closeDialog = () => {
// render(null, container)
// document.body.removeChild(container)
// }
// // 创建虚拟节点
// const vnode = createVNode(ssDialog, {
// levelUserIds,
// modelValue: true, // 确保初始显示
// 'onUpdate:visible': val => {
// if (!val) closeDialog()
// },
// onConfirm: data => {
// closeDialog()
// resolve(data)
// },
// onCancel: () => {
// closeDialog()
// resolve(null)
// },
// })
// // 渲染到 DOM
// render(vnode, container)
// })
// }
/**
* 保存送审数据(示例,可与你的后端对应)
*/
async function save_ss_data(
ModuleID,
userid,
savetable,
ywid,
oldstate,
state,
tosh_useid,
sh_memo,
func,
) {
const updateRows = [];
const insert_sh_records = [];
const obj = { b_id: ywid, b_state: state };
updateRows.push(obj);
const inputdatetime = dayjs().format("YYYY-MM-DD HH:mm:ss");
insert_sh_records.push({
b_module_id: ModuleID,
b_user_id: userid,
b_level: "0",
b_name: state,
b_dj_id: ywid,
b_sh: "1",
b_sh_date: inputdatetime,
b_sh_memo: sh_memo,
b_tosh_useid: tosh_useid,
});
const requestData = [];
if (updateRows.length > 0) {
requestData.push({
tableName: savetable,
keyField: "b_id",
inserts: [],
updates: updateRows,
deletes: [],
});
requestData.push({
tableName: "sh_record",
keyField: "b_id",
inserts: insert_sh_records,
updates: [],
deletes: [],
});
if (savetable === "cw_zdorder") {
const updateFeeData = [];
const zdidarr = updateRows.map((row) => `'${row.b_id}'`);
// 获取账单状态
const getBStateById = (id) => {
const row = updateRows.find((r) => r.b_id === id);
return row ? row.b_state : "";
};
// 这里替代 loaddatas()
const searchsql = `isnull(mx_zdorderid, '') in (${zdidarr.join(",")})`;
const cwres = await axios.get("/selectlistbysql", {
params: {
sql: `select * from bs_business_cw where ${searchsql} order by subid`,
},
});
const cwrows = cwres.data?.data?.records || [];
for (const cw of cwrows) {
const zdstate = getBStateById(cw.mx_zdorderid) || state;
updateFeeData.push({
subid: cw.subid,
father: cw.father,
mx_zdorder_state: zdstate,
});
}
if (updateFeeData.length > 0) {
requestData.push({
tableName: "bs_business_cw",
keyField: "subid",
inserts: [],
updates: updateFeeData,
deletes: [],
});
}
}
}
const response = await axios.post("/saveobjectV2", requestData, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success(state + "成功!");
if (typeof func === "function") {
await func();
}
} else {
throw new Error(response.data?.message || "保存失败");
}
}
export const mod_shcenter_bh = async (ywid, savetable, ModuleID, state, userid, func) => {
// 加载业务数据
let searchsql = `select * from ${savetable} where b_id='${ywid}' order by b_id`;
const ywdatasRes = await axios.get("/selectlistbysql", { params: { sql: searchsql } });
const ywdatas = ywdatasRes.data?.data?.records || [];
if (!ywdatas.length) {
ElMessage.error("当前业务单号不存在");
return false;
}
const rowdata = ywdatas[0];
const ywownerid = rowdata.b_owner_id;
const nowstate = rowdata.b_state;
if (nowstate === "审核驳回") {
ElMessage.warning("已驳回!请检查!");
return false;
}
// 查询审核记录
const shSql = `select * from sh_record where b_dj_id='${ywid}' and b_module_id='${ModuleID}' order by b_id desc`;
const shRes = await axios.get("/selectlistbysql", { params: { sql: shSql } });
const shrecords = shRes.data?.data?.records || [];
let now_level = 0;
if (!shrecords || shrecords.length === 0) {
// 没有记录时判断权限
const shSql1 = `select * from sh_b_set where b_module_id='${ModuleID}' and b_user_id='${userid}' order by b_level`;
const shRes1 = await axios.get("/selectlistbysql", { params: { sql: shSql1 } });
const shsetdatas = shRes1.data?.data?.records || [];
if (!shsetdatas || shsetdatas.length === 0) {
ElMessage.warning("你没有该票业务的驳回权限");
return false;
}
} else {
now_level = shrecords[0].b_level;
const dsh_useid = shrecords[0].b_tosh_useid;
const sh_userid = shrecords[0].b_user_id;
if (dsh_useid !== userid && sh_userid !== userid && now_level > 0) {
ElMessage.warning("你没有该票业务的驳回权限");
return false;
}
if (nowstate === "送审" && dsh_useid !== userid) {
ElMessage.warning("你没有该票业务的驳回权限");
return false;
}
}
// === 5. 弹出驳回弹窗 ===
const data = await openBHDialog("", ywownerid);
if (!data) {
ElMessage.info("已取消驳回");
return;
}
await save_bh_data(
ModuleID,
userid,
savetable,
ywid,
state,
now_level,
data.user,
data.memo,
func,
);
};
// export function openBHDialog(levelUserIds, ywownerid) {
// return new Promise(resolve => {
// const container = document.createElement('div')
// document.body.appendChild(container)
// const closeDialog = () => {
// render(null, container)
// document.body.removeChild(container)
// }
// const vnode = createVNode(bhDialog, {
// modelValue: true,
// levelUserIds,
// ywownerid,
// onConfirm: data => {
// closeDialog()
// resolve(data)
// },
// onCancel: () => {
// closeDialog()
// resolve(null)
// }
// })
// render(vnode, container)
// })
// }
async function save_bh_data(
ModuleID,
userid,
savetable,
ywid,
state,
now_level,
tosh_useid,
sh_memo,
func,
) {
const updateRows = [];
const insert_sh_records = [];
const inputdatetime = dayjs().format("YYYY-MM-DD HH:mm:ss");
const obj = { b_id: ywid, b_state: state };
updateRows.push(obj);
insert_sh_records.push({
b_module_id: ModuleID,
b_user_id: userid,
b_level: now_level,
b_name: state,
b_dj_id: ywid,
b_sh: "0",
b_sh_date: inputdatetime,
b_sh_memo: sh_memo,
b_tosh_useid: tosh_useid,
});
const requestData = [];
if (updateRows.length > 0) {
requestData.push({
tableName: savetable,
keyField: "b_id",
inserts: [],
updates: updateRows,
deletes: [],
});
requestData.push({
tableName: "sh_record",
keyField: "b_id",
inserts: insert_sh_records,
updates: [],
deletes: [],
});
if (savetable === "cw_zdorder") {
const updateFeeData = [];
const zdidarr = updateRows.map((row) => `'${row.b_id}'`);
// 获取账单状态
const getBStateById = (id) => {
const row = updateRows.find((r) => r.b_id === id);
return row ? row.b_state : "";
};
// 这里替代 loaddatas()
const searchsql = `isnull(mx_zdorderid, '') in (${zdidarr.join(",")})`;
const cwres = await axios.get("/selectlistbysql", {
params: {
sql: `select * from bs_business_cw where ${searchsql} order by subid`,
},
});
const cwrows = cwres.data?.data?.records || [];
for (const cw of cwrows) {
const zdstate = getBStateById(cw.mx_zdorderid) || state;
updateFeeData.push({
subid: cw.subid,
father: cw.father,
mx_zdorder_state: zdstate,
});
}
if (updateFeeData.length > 0) {
requestData.push({
tableName: "bs_business_cw",
keyField: "subid",
inserts: [],
updates: updateFeeData,
deletes: [],
});
}
}
}
const response = await axios.post("/saveobjectV2", requestData, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success(state + "成功!");
if (typeof func === "function") {
await func();
}
} else {
throw new Error(response.data?.message || "保存失败");
}
}
export const modgrididstate = async (rows, savetable, ModuleID, state, func) => {
if (!rows || rows.length === 0) {
ElMessage.warning("请勾选要送审的记录!");
return;
}
// 状态可变范围
let canuse_state = [];
if (state === "送审") canuse_state = ["草拟", "审核驳回", ""];
if (state === "归档") canuse_state = ["审核通过", "完成"];
// 时间辅助
const inputdatetime = ["cw_zdorder", "cw_zdorder_f"].includes(ModuleID)
? dayjs().format("YYYY-MM-DD HH:mm:ss")
: "";
const updateFeeData = [];
const updateRows = [];
// 遍历勾选行
for (const row of rows) {
if (!row) return;
const oldstate = row.b_state || row.b_State;
const allowUpdate = canuse_state.length === 0 || canuse_state.includes(oldstate);
if (!allowUpdate) {
ElMessage.warning(`只有 [${canuse_state.join(",")}] 的记录才能 ${state}!`);
return;
}
// 针对 cw_zdorder 模块的附表更新逻辑
if (["cw_zdorder", "cw_zdorder_f"].includes(ModuleID)) {
let searchsql = `select * from bs_business_cw where isnull(mx_zdorderid,'')='${row.b_id}' order by subid`;
const response = await axios.get("/selectlistbysql", { params: { sql: searchsql } });
if (response.data?.success) {
const records = response.data.data.records || [];
if (records.length > 0) {
for (const cw of records) {
updateFeeData.push({
subid: cw.subid,
father: cw.father,
mx_zdorder_state: state,
});
}
}
}
// 审核驳回时校验费用
if (state === "审核驳回") {
const sql = `select count(*) as cnt,sum(case when isnull(mx_invoiceid,'')<>'' or isnull(mx_sumamount,0)<>0 then 1 else 0 end) as un_cnt from bs_business_cw where mx_zdorderid='${row.b_id}'`;
const response = await axios.get("/selectlistbysql", { params: { sql } });
if (response.data?.success) {
const records = response.data.data.records || [];
if (records.length > 0) {
if (records[0].cnt > 0 && records[0].cnt === records[0].un_cnt) {
ElMessage.error("驳回失败!费用项已销账或开票!");
return;
}
}
}
}
}
// 针对仓库模块的校验
if (["ck_in", "ck_out"].includes(ModuleID)) {
if (state === "审核驳回" && row.b_depotconfirm === "1") {
ElMessage.error("驳回失败!仓库确认数据不能驳回!");
return;
}
}
const obj = { b_id: row.b_id, b_state: state };
updateRows.push(obj);
}
const requestData = [];
if (updateRows.length > 0) {
requestData.push({
tableName: savetable,
keyField: "b_id",
inserts: [],
updates: updateRows,
deletes: [],
});
if (updateFeeData.length > 0) {
requestData.push({
tableName: "bs_business_cw",
keyField: "subid",
inserts: [],
updates: updateFeeData,
deletes: [],
});
}
}
const response = await axios.post("/saveobjectV2", requestData, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success(state + "成功!");
if (typeof func === "function") {
await func();
}
} else {
throw new Error(response.data?.message || "保存失败");
}
};
// ---------------------------
export async function tosh(row, moduleid, tablename, func) {
try {
if (row.b_state !== "草拟" && !row.b_state.includes("驳回")) {
ElMessage.warning("只有状态为草拟或驳回的数据才能进行送审");
return;
}
let strid = row.b_id;
// === 5. 弹出选择审批人弹窗 ===
const data = await opentoshDialog();
if (!data) {
ElMessage.info("已取消送审");
return;
}
// === 7. 调用保存函数 ===
await save_tosh_data(moduleid, tablename, strid, data.memo, func);
} catch (err) {
console.error(err);
ElMessage.error("送审失败");
}
}
export function opentoshDialog(levelUserIds, ywownerid) {
return new Promise((resolve) => {
const container = document.createElement("div");
document.body.appendChild(container);
const closeDialog = () => {
render(null, container);
document.body.removeChild(container);
};
const vnode = createVNode(toshDialog, {
modelValue: true,
onConfirm: (data) => {
closeDialog();
resolve(data);
},
onCancel: () => {
closeDialog();
resolve(null);
},
});
render(vnode, container);
});
}
async function save_tosh_data(ModuleID, tablename, strid, sh_memo, func) {
const userStore = useUserStore();
let sstate = "";
let jobj = [];
let recordrows = [];
let wxsenddatas = [];
let thistime = dayjs().format("YYYY-MM-DD HH:mm:ss");
const res = await getDataBySql(`select * from ${tablename} where b_id='${strid}' order by b_id`);
if (res.records.length > 0) {
for (let item of res.records) {
const res1 = await getDataBySql(`select b_state from ${tablename} where b_id='${item.b_id}'`);
if (res1.records.length > 0) {
sstate = res1.records[0].b_state;
}
if (sstate != "草拟" && sstate.indexOf("驳回") <= -1) {
ElMessage.warning("只有状态为草拟或驳回的数据才能进行送审");
return;
}
let jtmp = {};
jtmp.b_id = item.b_id;
jtmp.b_state = "送审";
jobj.push(jtmp);
var recrow = {};
recrow["b_module_id"] = ModuleID;
recrow["b_user_id"] = userStore.userInfo.userid;
recrow["b_dj_id"] = item.b_id;
recrow["b_level"] = "0";
recrow["b_sh"] = "1";
recrow["b_name"] = "送审";
recrow["b_sh_date"] = thistime;
recrow["b_sh_memo"] = sh_memo;
recordrows.push(recrow);
let modname = "";
const res = await getDataBySql(`select b_ChineseName from s_module where b_id='${ModuleID}'`);
if (res.records.length > 0) {
modname = res.records[0].b_ChineseName;
}
const ds_datas = await getDataBySql(
`select sh_b_set.b_condition,b_user_id,b_user.b_phone,sh_b_set.b_level,b_user.b_password from sh_b_set left join b_user on sh_b_set.b_user_id = b_user.b_id where b_module_id='${ModuleID}' and b_level='1'`,
);
if (ds_datas.records.length > 0) {
for (const k of ds_datas.records) {
if (k.b_phone != "") {
let canuse = true;
let condition = k.b_condition;
let mobileids = k.b_phone;
if (condition) {
const res1 = await getDataBySql(
`select * from ${tablename} where b_id='${item.b_id}' and ${condition}`,
);
if (res1.records.length == 0) {
canuse = false;
}
if (canuse) {
var obj = {};
obj["mobileids"] = mobileids;
obj["b_id"] = item.b_id;
obj["modid"] = modname;
obj["b_owner_id"] = userStore.userInfo.userid;
obj["orgid"] = userStore.userInfo.orgid;
obj["userid"] = k.b_user_id;
obj["password"] = k.b_password;
wxsenddatas.push(obj);
}
}
}
}
}
}
}
const requestData = [
{
tableName: "sh_record",
keyField: "b_id",
inserts: recordrows,
updates: [],
deletes: [],
},
{
tableName: tablename,
keyField: "b_id",
inserts: [],
updates: jobj,
deletes: [],
},
];
const response = await axios.post("/saveobjectV2", requestData, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success("送审成功!");
if (typeof func === "function") {
await func();
}
} else {
throw new Error(response.data?.message || "保存失败");
}
}
export async function shcenter_cs(rows, ModuleID, tablename, func) {
const userStore = useUserStore();
let sstate = "";
let jobj = [];
let recordrows = [];
let thistime = dayjs().format("YYYY-MM-DD HH:mm:ss");
const res1 = await getDataBySql(`select b_state from ${tablename} where b_id='${rows.b_id}'`);
if (res1.records.length > 0) {
sstate = res1.records[0].b_state;
}
if (sstate != "送审") {
ElMessage.warning("只有状态为送审的数据才能进行撤审!");
return;
}
let jtmp = {};
jtmp.b_id = rows.b_id;
jtmp.b_state = "草拟";
jobj.push(jtmp);
var recrow = {};
recrow["b_module_id"] = ModuleID;
recrow["b_user_id"] = userStore.userInfo.userid;
recrow["b_dj_id"] = rows.b_id;
recrow["b_level"] = "0";
recrow["b_sh"] = "0";
recrow["b_name"] = "撤销送审";
recrow["b_sh_date"] = thistime;
recrow["b_sh_memo"] = "";
recordrows.push(recrow);
const requestData = [
{
tableName: "sh_record",
keyField: "b_id",
inserts: recordrows,
updates: [],
deletes: [],
},
{
tableName: tablename,
keyField: "b_id",
inserts: [],
updates: jobj,
deletes: [],
},
];
const response = await axios.post("/saveobjectV2", requestData, {
headers: { "Content-Type": "application/json" },
});
if (response.data?.success) {
ElMessage.success("撤审成功!");
if (typeof func === "function") {
await func();
}
} else {
throw new Error(response.data?.message || "保存失败");
}
}
+109
View File
@@ -0,0 +1,109 @@
/**
* 高精度数学工具。
*
* 注意:
* - add / subtract / multiply / divide 返回的是 Big 包装对象,不是普通 number/string。
* - 需要写入表单、展示到页面或提交字符串金额时,必须调用 `.toFixed()` 转成字符串。
* - 空值、null、undefined、非法数字会按 0 处理。
*
* 示例:
* math.multiply(price, qty).toFixed()
* math.divide(amount, rate).toFixed(4)
*/
import Big from "big.js";
let defaultDecimal = 2;
function getDecimal(decimal = defaultDecimal) {
const value = Number(decimal);
return Number.isInteger(value) && value >= 0 ? value : defaultDecimal;
}
class MathBig extends Big {
toFixed(decimal = defaultDecimal) {
return super.toFixed(getDecimal(decimal));
}
}
function toBig(value) {
try {
if (value == null || value === "") return new Big(0);
if (value instanceof Big) return value;
return new Big(String(value).trim());
} catch {
return new Big(0);
}
}
function wrap(value) {
return new MathBig(value);
}
const math = {
/**
* 设置默认小数位数。
* @param {number|string} decimal 默认保留的小数位数,非法值会回退为当前默认值。
* @returns {typeof math} 返回 math,方便链式调用。
*/
setDecimal(decimal) {
defaultDecimal = getDecimal(decimal);
return this;
},
/**
* 加法。
* @param {...(number|string|Big|null|undefined)} values 需要相加的值,支持多个参数。
* @returns {MathBig} 高精度结果;转为字符串必须调用 `.toFixed()`。
*/
add(...values) {
return wrap(values.reduce((total, value) => total.plus(toBig(value)), new Big(0)));
},
/**
* 减法。
* @param {number|string|Big|null|undefined} firstValue 被减数。
* @param {...(number|string|Big|null|undefined)} values 减数,支持多个参数。
* @returns {MathBig} 高精度结果;转为字符串必须调用 `.toFixed()`。
*/
subtract(firstValue, ...values) {
return wrap(values.reduce((total, value) => total.minus(toBig(value)), toBig(firstValue)));
},
/**
* 乘法。
* @param {...(number|string|Big|null|undefined)} values 需要相乘的值,支持多个参数。
* @returns {MathBig} 高精度结果;转为字符串必须调用 `.toFixed()`。
*/
multiply(...values) {
return wrap(values.reduce((total, value) => total.times(toBig(value)), new Big(1)));
},
/**
* 除法。
* @param {number|string|Big|null|undefined} firstValue 被除数。
* @param {...(number|string|Big|null|undefined)} values 除数,支持多个参数;除数为 0 时本次结果返回 0。
* @returns {MathBig} 高精度结果;转为字符串必须调用 `.toFixed()`。
*/
divide(firstValue, ...values) {
return wrap(
values.reduce((total, value) => {
const divisor = toBig(value);
return divisor.eq(0) ? new Big(0) : total.div(divisor);
}, toBig(firstValue)),
);
},
/**
* 将任意数值按指定小数位转成字符串。
* @param {number|string|Big|null|undefined} value 需要格式化的值。
* @param {number|string} [decimal] 小数位数;不传则使用默认小数位。
* @returns {string} 格式化后的字符串。
*/
toFixed(value, decimal) {
return toBig(value).toFixed(decimal);
},
};
export default math;
@@ -0,0 +1,31 @@
// permission.js
import { useUserStore } from "../store/user";
export default {
install(app) {
function updatePermission(el, permissionId) {
const userStore = useUserStore();
const disabled = !userStore.hasActionPermission(permissionId);
// 原生支持 disabled 的元素
if ("disabled" in el) {
el.disabled = disabled;
}
// 通用处理
el.style.pointerEvents = disabled ? "none" : "";
el.style.opacity = disabled ? "0.6" : "";
el.setAttribute("aria-disabled", disabled ? "true" : "false");
}
app.directive("permission", {
mounted(el, binding) {
updatePermission(el, binding.value);
},
updated(el, binding) {
updatePermission(el, binding.value);
},
});
},
};
+64
View File
@@ -0,0 +1,64 @@
import dayjs from "dayjs";
export function formatToDate(date) {
const validDate = dayjs(date);
if (!validDate.isValid()) {
return date;
}
return validDate.format("YYYY-MM-DD");
}
export function formatToDateTime(date) {
const validDate = dayjs(date);
if (!validDate.isValid()) {
return date;
}
return validDate.format("YYYY-MM-DD HH:mm:ss");
}
export function getDateTime() {
return dayjs().format("YYYY-MM-DD HH:mm:ss");
}
export function getDate() {
return dayjs().format("YYYY-MM-DD");
}
export function upperCase(str) {
if (typeof str === "string") {
return str.toUpperCase();
}
if (str != null && typeof str.toString === "function") {
return str.toString().toUpperCase();
}
return str;
}
export function getcheckdjid(strvalid = "") {
if (strvalid == undefined) {
strvalid = "";
}
let tmpid = "";
let djarr = strvalid.split("");
// var x=/^[a-zA-Z0-9\u4e00-\u9fa5-_]+$/i;
let sfuhao = '\/*:?<>"|&';
for (let j in djarr) {
let tmp = djarr[j];
if (!sfuhao.includes(tmp)) {
tmpid = tmpid + tmp;
} else {
tmpid = `${tmpid} `;
}
}
return tmpid;
}
// 默认导出所有方法
export default {
getDate,
getDateTime,
upperCase,
formatToDate,
formatToDateTime,
getcheckdjid,
};