Commit 5f91557b by 李宁

1Merge branch 'channelBusi'

2 parents f585baf0 637c146e
Showing 60 changed files with 8869 additions and 2294 deletions
{
"permissions": {
"allow": [
"Bash(grep -E \"\\.(js|vue|html)$\")",
"Bash(npm install axios)",
"Bash(tree src/)",
"Bash(npm run build)",
"Bash(npm run build-only)",
"Bash(npm run type-check)"
],
"deny": [],
"ask": []
}
}
\ No newline at end of file
import {
add_location_default,
aim_default,
alarm_clock_default,
apple_default,
arrow_down_bold_default,
arrow_down_default,
arrow_left_bold_default,
arrow_left_default,
arrow_right_bold_default,
arrow_right_default,
arrow_up_bold_default,
arrow_up_default,
avatar_default,
back_default,
baseball_default,
basketball_default,
bell_default,
bell_filled_default,
bicycle_default,
bottom_default,
bottom_left_default,
bottom_right_default,
bowl_default,
box_default,
briefcase_default,
brush_default,
brush_filled_default,
burger_default,
calendar_default,
camera_default,
camera_filled_default,
caret_bottom_default,
caret_left_default,
caret_right_default,
caret_top_default,
cellphone_default,
chat_dot_round_default,
chat_dot_square_default,
chat_line_round_default,
chat_line_square_default,
chat_round_default,
chat_square_default,
check_default,
checked_default,
cherry_default,
chicken_default,
chrome_filled_default,
circle_check_default,
circle_check_filled_default,
circle_close_default,
circle_close_filled_default,
circle_plus_default,
circle_plus_filled_default,
clock_default,
close_bold_default,
close_default,
cloudy_default,
coffee_cup_default,
coffee_default,
coin_default,
cold_drink_default,
collection_default,
collection_tag_default,
comment_default,
compass_default,
connection_default,
coordinate_default,
copy_document_default,
cpu_default,
credit_card_default,
crop_default,
d_arrow_left_default,
d_arrow_right_default,
d_caret_default,
data_analysis_default,
data_board_default,
data_line_default,
delete_default,
delete_filled_default,
delete_location_default,
dessert_default,
discount_default,
dish_default,
dish_dot_default,
document_add_default,
document_checked_default,
document_copy_default,
document_default,
document_delete_default,
document_remove_default,
download_default,
drizzling_default,
edit_default,
edit_pen_default,
eleme_default,
eleme_filled_default,
element_plus_default,
expand_default,
failed_default,
female_default,
files_default,
film_default,
filter_default,
finished_default,
first_aid_kit_default,
flag_default,
fold_default,
folder_add_default,
folder_checked_default,
folder_default,
folder_delete_default,
folder_opened_default,
folder_remove_default,
food_default,
football_default,
fork_spoon_default,
fries_default,
full_screen_default,
goblet_default,
goblet_full_default,
goblet_square_default,
goblet_square_full_default,
gold_medal_default,
goods_default,
goods_filled_default,
grape_default,
grid_default,
guide_default,
handbag_default,
headset_default,
help_default,
help_filled_default,
hide_default,
histogram_default,
home_filled_default,
hot_water_default,
house_default,
ice_cream_default,
ice_cream_round_default,
ice_cream_square_default,
ice_drink_default,
ice_tea_default,
info_filled_default,
iphone_default,
key_default,
knife_fork_default,
lightning_default,
link_default,
list_default,
loading_default,
location_default,
location_filled_default,
location_information_default,
lock_default,
lollipop_default,
magic_stick_default,
magnet_default,
male_default,
management_default,
map_location_default,
medal_default,
memo_default,
menu_default,
message_box_default,
message_default,
mic_default,
microphone_default,
milk_tea_default,
minus_default,
money_default,
monitor_default,
moon_default,
moon_night_default,
more_default,
more_filled_default,
mostly_cloudy_default,
mouse_default,
mug_default,
mute_default,
mute_notification_default,
no_smoking_default,
notebook_default,
notification_default,
odometer_default,
office_building_default,
open_default,
operation_default,
opportunity_default,
orange_default,
paperclip_default,
partly_cloudy_default,
pear_default,
phone_default,
phone_filled_default,
picture_default,
picture_filled_default,
picture_rounded_default,
pie_chart_default,
place_default,
platform_default,
plus_default,
pointer_default,
position_default,
postcard_default,
pouring_default,
present_default,
price_tag_default,
printer_default,
promotion_default,
quartz_watch_default,
question_filled_default,
rank_default,
reading_default,
reading_lamp_default,
refresh_default,
refresh_left_default,
refresh_right_default,
refrigerator_default,
remove_default,
remove_filled_default,
right_default,
scale_to_original_default,
school_default,
scissor_default,
search_default,
select_default,
sell_default,
semi_select_default,
service_default,
set_up_default,
setting_default,
share_default,
ship_default,
shop_default,
shopping_bag_default,
shopping_cart_default,
shopping_cart_full_default,
shopping_trolley_default,
smoking_default,
soccer_default,
sold_out_default,
sort_default,
sort_down_default,
sort_up_default,
stamp_default,
star_default,
star_filled_default,
stopwatch_default,
success_filled_default,
sugar_default,
suitcase_default,
suitcase_line_default,
sunny_default,
sunrise_default,
sunset_default,
switch_button_default,
switch_default,
switch_filled_default,
takeaway_box_default,
ticket_default,
tickets_default,
timer_default,
toilet_paper_default,
tools_default,
top_default,
top_left_default,
top_right_default,
trend_charts_default,
trophy_base_default,
trophy_default,
turn_off_default,
umbrella_default,
unlock_default,
upload_default,
upload_filled_default,
user_default,
user_filled_default,
van_default,
video_camera_default,
video_camera_filled_default,
video_pause_default,
video_play_default,
view_default,
wallet_default,
wallet_filled_default,
warn_triangle_filled_default,
warning_default,
warning_filled_default,
watch_default,
watermelon_default,
wind_power_default,
zoom_in_default,
zoom_out_default
} from "./chunk-XAE367SZ.js";
import "./chunk-YHMWYXEE.js";
import "./chunk-G3PMV62Z.js";
export {
add_location_default as AddLocation,
aim_default as Aim,
alarm_clock_default as AlarmClock,
apple_default as Apple,
arrow_down_default as ArrowDown,
arrow_down_bold_default as ArrowDownBold,
arrow_left_default as ArrowLeft,
arrow_left_bold_default as ArrowLeftBold,
arrow_right_default as ArrowRight,
arrow_right_bold_default as ArrowRightBold,
arrow_up_default as ArrowUp,
arrow_up_bold_default as ArrowUpBold,
avatar_default as Avatar,
back_default as Back,
baseball_default as Baseball,
basketball_default as Basketball,
bell_default as Bell,
bell_filled_default as BellFilled,
bicycle_default as Bicycle,
bottom_default as Bottom,
bottom_left_default as BottomLeft,
bottom_right_default as BottomRight,
bowl_default as Bowl,
box_default as Box,
briefcase_default as Briefcase,
brush_default as Brush,
brush_filled_default as BrushFilled,
burger_default as Burger,
calendar_default as Calendar,
camera_default as Camera,
camera_filled_default as CameraFilled,
caret_bottom_default as CaretBottom,
caret_left_default as CaretLeft,
caret_right_default as CaretRight,
caret_top_default as CaretTop,
cellphone_default as Cellphone,
chat_dot_round_default as ChatDotRound,
chat_dot_square_default as ChatDotSquare,
chat_line_round_default as ChatLineRound,
chat_line_square_default as ChatLineSquare,
chat_round_default as ChatRound,
chat_square_default as ChatSquare,
check_default as Check,
checked_default as Checked,
cherry_default as Cherry,
chicken_default as Chicken,
chrome_filled_default as ChromeFilled,
circle_check_default as CircleCheck,
circle_check_filled_default as CircleCheckFilled,
circle_close_default as CircleClose,
circle_close_filled_default as CircleCloseFilled,
circle_plus_default as CirclePlus,
circle_plus_filled_default as CirclePlusFilled,
clock_default as Clock,
close_default as Close,
close_bold_default as CloseBold,
cloudy_default as Cloudy,
coffee_default as Coffee,
coffee_cup_default as CoffeeCup,
coin_default as Coin,
cold_drink_default as ColdDrink,
collection_default as Collection,
collection_tag_default as CollectionTag,
comment_default as Comment,
compass_default as Compass,
connection_default as Connection,
coordinate_default as Coordinate,
copy_document_default as CopyDocument,
cpu_default as Cpu,
credit_card_default as CreditCard,
crop_default as Crop,
d_arrow_left_default as DArrowLeft,
d_arrow_right_default as DArrowRight,
d_caret_default as DCaret,
data_analysis_default as DataAnalysis,
data_board_default as DataBoard,
data_line_default as DataLine,
delete_default as Delete,
delete_filled_default as DeleteFilled,
delete_location_default as DeleteLocation,
dessert_default as Dessert,
discount_default as Discount,
dish_default as Dish,
dish_dot_default as DishDot,
document_default as Document,
document_add_default as DocumentAdd,
document_checked_default as DocumentChecked,
document_copy_default as DocumentCopy,
document_delete_default as DocumentDelete,
document_remove_default as DocumentRemove,
download_default as Download,
drizzling_default as Drizzling,
edit_default as Edit,
edit_pen_default as EditPen,
eleme_default as Eleme,
eleme_filled_default as ElemeFilled,
element_plus_default as ElementPlus,
expand_default as Expand,
failed_default as Failed,
female_default as Female,
files_default as Files,
film_default as Film,
filter_default as Filter,
finished_default as Finished,
first_aid_kit_default as FirstAidKit,
flag_default as Flag,
fold_default as Fold,
folder_default as Folder,
folder_add_default as FolderAdd,
folder_checked_default as FolderChecked,
folder_delete_default as FolderDelete,
folder_opened_default as FolderOpened,
folder_remove_default as FolderRemove,
food_default as Food,
football_default as Football,
fork_spoon_default as ForkSpoon,
fries_default as Fries,
full_screen_default as FullScreen,
goblet_default as Goblet,
goblet_full_default as GobletFull,
goblet_square_default as GobletSquare,
goblet_square_full_default as GobletSquareFull,
gold_medal_default as GoldMedal,
goods_default as Goods,
goods_filled_default as GoodsFilled,
grape_default as Grape,
grid_default as Grid,
guide_default as Guide,
handbag_default as Handbag,
headset_default as Headset,
help_default as Help,
help_filled_default as HelpFilled,
hide_default as Hide,
histogram_default as Histogram,
home_filled_default as HomeFilled,
hot_water_default as HotWater,
house_default as House,
ice_cream_default as IceCream,
ice_cream_round_default as IceCreamRound,
ice_cream_square_default as IceCreamSquare,
ice_drink_default as IceDrink,
ice_tea_default as IceTea,
info_filled_default as InfoFilled,
iphone_default as Iphone,
key_default as Key,
knife_fork_default as KnifeFork,
lightning_default as Lightning,
link_default as Link,
list_default as List,
loading_default as Loading,
location_default as Location,
location_filled_default as LocationFilled,
location_information_default as LocationInformation,
lock_default as Lock,
lollipop_default as Lollipop,
magic_stick_default as MagicStick,
magnet_default as Magnet,
male_default as Male,
management_default as Management,
map_location_default as MapLocation,
medal_default as Medal,
memo_default as Memo,
menu_default as Menu,
message_default as Message,
message_box_default as MessageBox,
mic_default as Mic,
microphone_default as Microphone,
milk_tea_default as MilkTea,
minus_default as Minus,
money_default as Money,
monitor_default as Monitor,
moon_default as Moon,
moon_night_default as MoonNight,
more_default as More,
more_filled_default as MoreFilled,
mostly_cloudy_default as MostlyCloudy,
mouse_default as Mouse,
mug_default as Mug,
mute_default as Mute,
mute_notification_default as MuteNotification,
no_smoking_default as NoSmoking,
notebook_default as Notebook,
notification_default as Notification,
odometer_default as Odometer,
office_building_default as OfficeBuilding,
open_default as Open,
operation_default as Operation,
opportunity_default as Opportunity,
orange_default as Orange,
paperclip_default as Paperclip,
partly_cloudy_default as PartlyCloudy,
pear_default as Pear,
phone_default as Phone,
phone_filled_default as PhoneFilled,
picture_default as Picture,
picture_filled_default as PictureFilled,
picture_rounded_default as PictureRounded,
pie_chart_default as PieChart,
place_default as Place,
platform_default as Platform,
plus_default as Plus,
pointer_default as Pointer,
position_default as Position,
postcard_default as Postcard,
pouring_default as Pouring,
present_default as Present,
price_tag_default as PriceTag,
printer_default as Printer,
promotion_default as Promotion,
quartz_watch_default as QuartzWatch,
question_filled_default as QuestionFilled,
rank_default as Rank,
reading_default as Reading,
reading_lamp_default as ReadingLamp,
refresh_default as Refresh,
refresh_left_default as RefreshLeft,
refresh_right_default as RefreshRight,
refrigerator_default as Refrigerator,
remove_default as Remove,
remove_filled_default as RemoveFilled,
right_default as Right,
scale_to_original_default as ScaleToOriginal,
school_default as School,
scissor_default as Scissor,
search_default as Search,
select_default as Select,
sell_default as Sell,
semi_select_default as SemiSelect,
service_default as Service,
set_up_default as SetUp,
setting_default as Setting,
share_default as Share,
ship_default as Ship,
shop_default as Shop,
shopping_bag_default as ShoppingBag,
shopping_cart_default as ShoppingCart,
shopping_cart_full_default as ShoppingCartFull,
shopping_trolley_default as ShoppingTrolley,
smoking_default as Smoking,
soccer_default as Soccer,
sold_out_default as SoldOut,
sort_default as Sort,
sort_down_default as SortDown,
sort_up_default as SortUp,
stamp_default as Stamp,
star_default as Star,
star_filled_default as StarFilled,
stopwatch_default as Stopwatch,
success_filled_default as SuccessFilled,
sugar_default as Sugar,
suitcase_default as Suitcase,
suitcase_line_default as SuitcaseLine,
sunny_default as Sunny,
sunrise_default as Sunrise,
sunset_default as Sunset,
switch_default as Switch,
switch_button_default as SwitchButton,
switch_filled_default as SwitchFilled,
takeaway_box_default as TakeawayBox,
ticket_default as Ticket,
tickets_default as Tickets,
timer_default as Timer,
toilet_paper_default as ToiletPaper,
tools_default as Tools,
top_default as Top,
top_left_default as TopLeft,
top_right_default as TopRight,
trend_charts_default as TrendCharts,
trophy_default as Trophy,
trophy_base_default as TrophyBase,
turn_off_default as TurnOff,
umbrella_default as Umbrella,
unlock_default as Unlock,
upload_default as Upload,
upload_filled_default as UploadFilled,
user_default as User,
user_filled_default as UserFilled,
van_default as Van,
video_camera_default as VideoCamera,
video_camera_filled_default as VideoCameraFilled,
video_pause_default as VideoPause,
video_play_default as VideoPlay,
view_default as View,
wallet_default as Wallet,
wallet_filled_default as WalletFilled,
warn_triangle_filled_default as WarnTriangleFilled,
warning_default as Warning,
warning_filled_default as WarningFilled,
watch_default as Watch,
watermelon_default as Watermelon,
wind_power_default as WindPower,
zoom_in_default as ZoomIn,
zoom_out_default as ZoomOut
};
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}
{
"hash": "655e7fe4",
"configHash": "92cf418e",
"lockfileHash": "d600e0a3",
"browserHash": "d2e0d31b",
"optimized": {
"vue": {
"src": "../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "b7821ec5",
"needsInterop": false
},
"pinia": {
"src": "../../node_modules/pinia/dist/pinia.mjs",
"file": "pinia.js",
"fileHash": "a659a454",
"needsInterop": false
},
"element-plus": {
"src": "../../node_modules/element-plus/es/index.mjs",
"file": "element-plus.js",
"fileHash": "13083701",
"needsInterop": false
},
"@element-plus/icons-vue": {
"src": "../../node_modules/@element-plus/icons-vue/dist/index.js",
"file": "@element-plus_icons-vue.js",
"fileHash": "d67fc749",
"needsInterop": false
},
"axios": {
"src": "../../node_modules/axios/index.js",
"file": "axios.js",
"fileHash": "e22e391f",
"needsInterop": false
},
"vue-router": {
"src": "../../node_modules/vue-router/dist/vue-router.mjs",
"file": "vue-router.js",
"fileHash": "30652b08",
"needsInterop": false
}
},
"chunks": {
"chunk-XAE367SZ": {
"file": "chunk-XAE367SZ.js"
},
"chunk-YHMWYXEE": {
"file": "chunk-YHMWYXEE.js"
},
"chunk-G3PMV62Z": {
"file": "chunk-G3PMV62Z.js"
}
}
}
\ No newline at end of file
import {
__export
} from "./chunk-G3PMV62Z.js";
// node_modules/axios/lib/helpers/bind.js
function bind(fn, thisArg) {
return function wrap() {
return fn.apply(thisArg, arguments);
};
}
// node_modules/axios/lib/utils.js
var { toString } = Object.prototype;
var { getPrototypeOf } = Object;
var { iterator, toStringTag } = Symbol;
var kindOf = /* @__PURE__ */ ((cache) => (thing) => {
const str = toString.call(thing);
return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
})(/* @__PURE__ */ Object.create(null));
var kindOfTest = (type) => {
type = type.toLowerCase();
return (thing) => kindOf(thing) === type;
};
var typeOfTest = (type) => (thing) => typeof thing === type;
var { isArray } = Array;
var isUndefined = typeOfTest("undefined");
function isBuffer(val) {
return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val);
}
var isArrayBuffer = kindOfTest("ArrayBuffer");
function isArrayBufferView(val) {
let result;
if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView) {
result = ArrayBuffer.isView(val);
} else {
result = val && val.buffer && isArrayBuffer(val.buffer);
}
return result;
}
var isString = typeOfTest("string");
var isFunction = typeOfTest("function");
var isNumber = typeOfTest("number");
var isObject = (thing) => thing !== null && typeof thing === "object";
var isBoolean = (thing) => thing === true || thing === false;
var isPlainObject = (val) => {
if (kindOf(val) !== "object") {
return false;
}
const prototype3 = getPrototypeOf(val);
return (prototype3 === null || prototype3 === Object.prototype || Object.getPrototypeOf(prototype3) === null) && !(toStringTag in val) && !(iterator in val);
};
var isEmptyObject = (val) => {
if (!isObject(val) || isBuffer(val)) {
return false;
}
try {
return Object.keys(val).length === 0 && Object.getPrototypeOf(val) === Object.prototype;
} catch (e) {
return false;
}
};
var isDate = kindOfTest("Date");
var isFile = kindOfTest("File");
var isBlob = kindOfTest("Blob");
var isFileList = kindOfTest("FileList");
var isStream = (val) => isObject(val) && isFunction(val.pipe);
var isFormData = (thing) => {
let kind;
return thing && (typeof FormData === "function" && thing instanceof FormData || isFunction(thing.append) && ((kind = kindOf(thing)) === "formdata" || // detect form-data instance
kind === "object" && isFunction(thing.toString) && thing.toString() === "[object FormData]"));
};
var isURLSearchParams = kindOfTest("URLSearchParams");
var [isReadableStream, isRequest, isResponse, isHeaders] = ["ReadableStream", "Request", "Response", "Headers"].map(kindOfTest);
var trim = (str) => str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
function forEach(obj, fn, { allOwnKeys = false } = {}) {
if (obj === null || typeof obj === "undefined") {
return;
}
let i;
let l;
if (typeof obj !== "object") {
obj = [obj];
}
if (isArray(obj)) {
for (i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj);
}
} else {
if (isBuffer(obj)) {
return;
}
const keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj);
const len = keys.length;
let key;
for (i = 0; i < len; i++) {
key = keys[i];
fn.call(null, obj[key], key, obj);
}
}
}
function findKey(obj, key) {
if (isBuffer(obj)) {
return null;
}
key = key.toLowerCase();
const keys = Object.keys(obj);
let i = keys.length;
let _key;
while (i-- > 0) {
_key = keys[i];
if (key === _key.toLowerCase()) {
return _key;
}
}
return null;
}
var _global = (() => {
if (typeof globalThis !== "undefined") return globalThis;
return typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : global;
})();
var isContextDefined = (context) => !isUndefined(context) && context !== _global;
function merge() {
const { caseless, skipUndefined } = isContextDefined(this) && this || {};
const result = {};
const assignValue = (val, key) => {
const targetKey = caseless && findKey(result, key) || key;
if (isPlainObject(result[targetKey]) && isPlainObject(val)) {
result[targetKey] = merge(result[targetKey], val);
} else if (isPlainObject(val)) {
result[targetKey] = merge({}, val);
} else if (isArray(val)) {
result[targetKey] = val.slice();
} else if (!skipUndefined || !isUndefined(val)) {
result[targetKey] = val;
}
};
for (let i = 0, l = arguments.length; i < l; i++) {
arguments[i] && forEach(arguments[i], assignValue);
}
return result;
}
var extend = (a, b, thisArg, { allOwnKeys } = {}) => {
forEach(b, (val, key) => {
if (thisArg && isFunction(val)) {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
}, { allOwnKeys });
return a;
};
var stripBOM = (content) => {
if (content.charCodeAt(0) === 65279) {
content = content.slice(1);
}
return content;
};
var inherits = (constructor, superConstructor, props, descriptors2) => {
constructor.prototype = Object.create(superConstructor.prototype, descriptors2);
constructor.prototype.constructor = constructor;
Object.defineProperty(constructor, "super", {
value: superConstructor.prototype
});
props && Object.assign(constructor.prototype, props);
};
var toFlatObject = (sourceObj, destObj, filter2, propFilter) => {
let props;
let i;
let prop;
const merged = {};
destObj = destObj || {};
if (sourceObj == null) return destObj;
do {
props = Object.getOwnPropertyNames(sourceObj);
i = props.length;
while (i-- > 0) {
prop = props[i];
if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) {
destObj[prop] = sourceObj[prop];
merged[prop] = true;
}
}
sourceObj = filter2 !== false && getPrototypeOf(sourceObj);
} while (sourceObj && (!filter2 || filter2(sourceObj, destObj)) && sourceObj !== Object.prototype);
return destObj;
};
var endsWith = (str, searchString, position) => {
str = String(str);
if (position === void 0 || position > str.length) {
position = str.length;
}
position -= searchString.length;
const lastIndex = str.indexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
var toArray = (thing) => {
if (!thing) return null;
if (isArray(thing)) return thing;
let i = thing.length;
if (!isNumber(i)) return null;
const arr = new Array(i);
while (i-- > 0) {
arr[i] = thing[i];
}
return arr;
};
var isTypedArray = /* @__PURE__ */ ((TypedArray) => {
return (thing) => {
return TypedArray && thing instanceof TypedArray;
};
})(typeof Uint8Array !== "undefined" && getPrototypeOf(Uint8Array));
var forEachEntry = (obj, fn) => {
const generator = obj && obj[iterator];
const _iterator = generator.call(obj);
let result;
while ((result = _iterator.next()) && !result.done) {
const pair = result.value;
fn.call(obj, pair[0], pair[1]);
}
};
var matchAll = (regExp, str) => {
let matches;
const arr = [];
while ((matches = regExp.exec(str)) !== null) {
arr.push(matches);
}
return arr;
};
var isHTMLForm = kindOfTest("HTMLFormElement");
var toCamelCase = (str) => {
return str.toLowerCase().replace(
/[-_\s]([a-z\d])(\w*)/g,
function replacer(m, p1, p2) {
return p1.toUpperCase() + p2;
}
);
};
var hasOwnProperty = (({ hasOwnProperty: hasOwnProperty2 }) => (obj, prop) => hasOwnProperty2.call(obj, prop))(Object.prototype);
var isRegExp = kindOfTest("RegExp");
var reduceDescriptors = (obj, reducer) => {
const descriptors2 = Object.getOwnPropertyDescriptors(obj);
const reducedDescriptors = {};
forEach(descriptors2, (descriptor, name) => {
let ret;
if ((ret = reducer(descriptor, name, obj)) !== false) {
reducedDescriptors[name] = ret || descriptor;
}
});
Object.defineProperties(obj, reducedDescriptors);
};
var freezeMethods = (obj) => {
reduceDescriptors(obj, (descriptor, name) => {
if (isFunction(obj) && ["arguments", "caller", "callee"].indexOf(name) !== -1) {
return false;
}
const value = obj[name];
if (!isFunction(value)) return;
descriptor.enumerable = false;
if ("writable" in descriptor) {
descriptor.writable = false;
return;
}
if (!descriptor.set) {
descriptor.set = () => {
throw Error("Can not rewrite read-only method '" + name + "'");
};
}
});
};
var toObjectSet = (arrayOrString, delimiter) => {
const obj = {};
const define = (arr) => {
arr.forEach((value) => {
obj[value] = true;
});
};
isArray(arrayOrString) ? define(arrayOrString) : define(String(arrayOrString).split(delimiter));
return obj;
};
var noop = () => {
};
var toFiniteNumber = (value, defaultValue) => {
return value != null && Number.isFinite(value = +value) ? value : defaultValue;
};
function isSpecCompliantForm(thing) {
return !!(thing && isFunction(thing.append) && thing[toStringTag] === "FormData" && thing[iterator]);
}
var toJSONObject = (obj) => {
const stack = new Array(10);
const visit = (source, i) => {
if (isObject(source)) {
if (stack.indexOf(source) >= 0) {
return;
}
if (isBuffer(source)) {
return source;
}
if (!("toJSON" in source)) {
stack[i] = source;
const target = isArray(source) ? [] : {};
forEach(source, (value, key) => {
const reducedValue = visit(value, i + 1);
!isUndefined(reducedValue) && (target[key] = reducedValue);
});
stack[i] = void 0;
return target;
}
}
return source;
};
return visit(obj, 0);
};
var isAsyncFn = kindOfTest("AsyncFunction");
var isThenable = (thing) => thing && (isObject(thing) || isFunction(thing)) && isFunction(thing.then) && isFunction(thing.catch);
var _setImmediate = ((setImmediateSupported, postMessageSupported) => {
if (setImmediateSupported) {
return setImmediate;
}
return postMessageSupported ? ((token, callbacks) => {
_global.addEventListener("message", ({ source, data }) => {
if (source === _global && data === token) {
callbacks.length && callbacks.shift()();
}
}, false);
return (cb) => {
callbacks.push(cb);
_global.postMessage(token, "*");
};
})(`axios@${Math.random()}`, []) : (cb) => setTimeout(cb);
})(
typeof setImmediate === "function",
isFunction(_global.postMessage)
);
var asap = typeof queueMicrotask !== "undefined" ? queueMicrotask.bind(_global) : typeof process !== "undefined" && process.nextTick || _setImmediate;
var isIterable = (thing) => thing != null && isFunction(thing[iterator]);
var utils_default = {
isArray,
isArrayBuffer,
isBuffer,
isFormData,
isArrayBufferView,
isString,
isNumber,
isBoolean,
isObject,
isPlainObject,
isEmptyObject,
isReadableStream,
isRequest,
isResponse,
isHeaders,
isUndefined,
isDate,
isFile,
isBlob,
isRegExp,
isFunction,
isStream,
isURLSearchParams,
isTypedArray,
isFileList,
forEach,
merge,
extend,
trim,
stripBOM,
inherits,
toFlatObject,
kindOf,
kindOfTest,
endsWith,
toArray,
forEachEntry,
matchAll,
isHTMLForm,
hasOwnProperty,
hasOwnProp: hasOwnProperty,
// an alias to avoid ESLint no-prototype-builtins detection
reduceDescriptors,
freezeMethods,
toObjectSet,
toCamelCase,
noop,
toFiniteNumber,
findKey,
global: _global,
isContextDefined,
isSpecCompliantForm,
toJSONObject,
isAsyncFn,
isThenable,
setImmediate: _setImmediate,
asap,
isIterable
};
// node_modules/axios/lib/core/AxiosError.js
function AxiosError(message, code, config, request, response) {
Error.call(this);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = new Error().stack;
}
this.message = message;
this.name = "AxiosError";
code && (this.code = code);
config && (this.config = config);
request && (this.request = request);
if (response) {
this.response = response;
this.status = response.status ? response.status : null;
}
}
utils_default.inherits(AxiosError, Error, {
toJSON: function toJSON() {
return {
// Standard
message: this.message,
name: this.name,
// Microsoft
description: this.description,
number: this.number,
// Mozilla
fileName: this.fileName,
lineNumber: this.lineNumber,
columnNumber: this.columnNumber,
stack: this.stack,
// Axios
config: utils_default.toJSONObject(this.config),
code: this.code,
status: this.status
};
}
});
var prototype = AxiosError.prototype;
var descriptors = {};
[
"ERR_BAD_OPTION_VALUE",
"ERR_BAD_OPTION",
"ECONNABORTED",
"ETIMEDOUT",
"ERR_NETWORK",
"ERR_FR_TOO_MANY_REDIRECTS",
"ERR_DEPRECATED",
"ERR_BAD_RESPONSE",
"ERR_BAD_REQUEST",
"ERR_CANCELED",
"ERR_NOT_SUPPORT",
"ERR_INVALID_URL"
// eslint-disable-next-line func-names
].forEach((code) => {
descriptors[code] = { value: code };
});
Object.defineProperties(AxiosError, descriptors);
Object.defineProperty(prototype, "isAxiosError", { value: true });
AxiosError.from = (error, code, config, request, response, customProps) => {
const axiosError = Object.create(prototype);
utils_default.toFlatObject(error, axiosError, function filter2(obj) {
return obj !== Error.prototype;
}, (prop) => {
return prop !== "isAxiosError";
});
const msg = error && error.message ? error.message : "Error";
const errCode = code == null && error ? error.code : code;
AxiosError.call(axiosError, msg, errCode, config, request, response);
if (error && axiosError.cause == null) {
Object.defineProperty(axiosError, "cause", { value: error, configurable: true });
}
axiosError.name = error && error.name || "Error";
customProps && Object.assign(axiosError, customProps);
return axiosError;
};
var AxiosError_default = AxiosError;
// node_modules/axios/lib/helpers/null.js
var null_default = null;
// node_modules/axios/lib/helpers/toFormData.js
function isVisitable(thing) {
return utils_default.isPlainObject(thing) || utils_default.isArray(thing);
}
function removeBrackets(key) {
return utils_default.endsWith(key, "[]") ? key.slice(0, -2) : key;
}
function renderKey(path, key, dots) {
if (!path) return key;
return path.concat(key).map(function each(token, i) {
token = removeBrackets(token);
return !dots && i ? "[" + token + "]" : token;
}).join(dots ? "." : "");
}
function isFlatArray(arr) {
return utils_default.isArray(arr) && !arr.some(isVisitable);
}
var predicates = utils_default.toFlatObject(utils_default, {}, null, function filter(prop) {
return /^is[A-Z]/.test(prop);
});
function toFormData(obj, formData, options) {
if (!utils_default.isObject(obj)) {
throw new TypeError("target must be an object");
}
formData = formData || new (null_default || FormData)();
options = utils_default.toFlatObject(options, {
metaTokens: true,
dots: false,
indexes: false
}, false, function defined(option, source) {
return !utils_default.isUndefined(source[option]);
});
const metaTokens = options.metaTokens;
const visitor = options.visitor || defaultVisitor;
const dots = options.dots;
const indexes = options.indexes;
const _Blob = options.Blob || typeof Blob !== "undefined" && Blob;
const useBlob = _Blob && utils_default.isSpecCompliantForm(formData);
if (!utils_default.isFunction(visitor)) {
throw new TypeError("visitor must be a function");
}
function convertValue(value) {
if (value === null) return "";
if (utils_default.isDate(value)) {
return value.toISOString();
}
if (utils_default.isBoolean(value)) {
return value.toString();
}
if (!useBlob && utils_default.isBlob(value)) {
throw new AxiosError_default("Blob is not supported. Use a Buffer instead.");
}
if (utils_default.isArrayBuffer(value) || utils_default.isTypedArray(value)) {
return useBlob && typeof Blob === "function" ? new Blob([value]) : Buffer.from(value);
}
return value;
}
function defaultVisitor(value, key, path) {
let arr = value;
if (value && !path && typeof value === "object") {
if (utils_default.endsWith(key, "{}")) {
key = metaTokens ? key : key.slice(0, -2);
value = JSON.stringify(value);
} else if (utils_default.isArray(value) && isFlatArray(value) || (utils_default.isFileList(value) || utils_default.endsWith(key, "[]")) && (arr = utils_default.toArray(value))) {
key = removeBrackets(key);
arr.forEach(function each(el, index) {
!(utils_default.isUndefined(el) || el === null) && formData.append(
// eslint-disable-next-line no-nested-ternary
indexes === true ? renderKey([key], index, dots) : indexes === null ? key : key + "[]",
convertValue(el)
);
});
return false;
}
}
if (isVisitable(value)) {
return true;
}
formData.append(renderKey(path, key, dots), convertValue(value));
return false;
}
const stack = [];
const exposedHelpers = Object.assign(predicates, {
defaultVisitor,
convertValue,
isVisitable
});
function build(value, path) {
if (utils_default.isUndefined(value)) return;
if (stack.indexOf(value) !== -1) {
throw Error("Circular reference detected in " + path.join("."));
}
stack.push(value);
utils_default.forEach(value, function each(el, key) {
const result = !(utils_default.isUndefined(el) || el === null) && visitor.call(
formData,
el,
utils_default.isString(key) ? key.trim() : key,
path,
exposedHelpers
);
if (result === true) {
build(el, path ? path.concat(key) : [key]);
}
});
stack.pop();
}
if (!utils_default.isObject(obj)) {
throw new TypeError("data must be an object");
}
build(obj);
return formData;
}
var toFormData_default = toFormData;
// node_modules/axios/lib/helpers/AxiosURLSearchParams.js
function encode(str) {
const charMap = {
"!": "%21",
"'": "%27",
"(": "%28",
")": "%29",
"~": "%7E",
"%20": "+",
"%00": "\0"
};
return encodeURIComponent(str).replace(/[!'()~]|%20|%00/g, function replacer(match) {
return charMap[match];
});
}
function AxiosURLSearchParams(params, options) {
this._pairs = [];
params && toFormData_default(params, this, options);
}
var prototype2 = AxiosURLSearchParams.prototype;
prototype2.append = function append(name, value) {
this._pairs.push([name, value]);
};
prototype2.toString = function toString2(encoder) {
const _encode = encoder ? function(value) {
return encoder.call(this, value, encode);
} : encode;
return this._pairs.map(function each(pair) {
return _encode(pair[0]) + "=" + _encode(pair[1]);
}, "").join("&");
};
var AxiosURLSearchParams_default = AxiosURLSearchParams;
// node_modules/axios/lib/helpers/buildURL.js
function encode2(val) {
return encodeURIComponent(val).replace(/%3A/gi, ":").replace(/%24/g, "$").replace(/%2C/gi, ",").replace(/%20/g, "+");
}
function buildURL(url, params, options) {
if (!params) {
return url;
}
const _encode = options && options.encode || encode2;
if (utils_default.isFunction(options)) {
options = {
serialize: options
};
}
const serializeFn = options && options.serialize;
let serializedParams;
if (serializeFn) {
serializedParams = serializeFn(params, options);
} else {
serializedParams = utils_default.isURLSearchParams(params) ? params.toString() : new AxiosURLSearchParams_default(params, options).toString(_encode);
}
if (serializedParams) {
const hashmarkIndex = url.indexOf("#");
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex);
}
url += (url.indexOf("?") === -1 ? "?" : "&") + serializedParams;
}
return url;
}
// node_modules/axios/lib/core/InterceptorManager.js
var InterceptorManager = class {
constructor() {
this.handlers = [];
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
use(fulfilled, rejected, options) {
this.handlers.push({
fulfilled,
rejected,
synchronous: options ? options.synchronous : false,
runWhen: options ? options.runWhen : null
});
return this.handlers.length - 1;
}
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*
* @returns {void}
*/
eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
}
/**
* Clear all interceptors from the stack
*
* @returns {void}
*/
clear() {
if (this.handlers) {
this.handlers = [];
}
}
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*
* @returns {void}
*/
forEach(fn) {
utils_default.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
}
};
var InterceptorManager_default = InterceptorManager;
// node_modules/axios/lib/defaults/transitional.js
var transitional_default = {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
};
// node_modules/axios/lib/platform/browser/classes/URLSearchParams.js
var URLSearchParams_default = typeof URLSearchParams !== "undefined" ? URLSearchParams : AxiosURLSearchParams_default;
// node_modules/axios/lib/platform/browser/classes/FormData.js
var FormData_default = typeof FormData !== "undefined" ? FormData : null;
// node_modules/axios/lib/platform/browser/classes/Blob.js
var Blob_default = typeof Blob !== "undefined" ? Blob : null;
// node_modules/axios/lib/platform/browser/index.js
var browser_default = {
isBrowser: true,
classes: {
URLSearchParams: URLSearchParams_default,
FormData: FormData_default,
Blob: Blob_default
},
protocols: ["http", "https", "file", "blob", "url", "data"]
};
// node_modules/axios/lib/platform/common/utils.js
var utils_exports = {};
__export(utils_exports, {
hasBrowserEnv: () => hasBrowserEnv,
hasStandardBrowserEnv: () => hasStandardBrowserEnv,
hasStandardBrowserWebWorkerEnv: () => hasStandardBrowserWebWorkerEnv,
navigator: () => _navigator,
origin: () => origin
});
var hasBrowserEnv = typeof window !== "undefined" && typeof document !== "undefined";
var _navigator = typeof navigator === "object" && navigator || void 0;
var hasStandardBrowserEnv = hasBrowserEnv && (!_navigator || ["ReactNative", "NativeScript", "NS"].indexOf(_navigator.product) < 0);
var hasStandardBrowserWebWorkerEnv = (() => {
return typeof WorkerGlobalScope !== "undefined" && // eslint-disable-next-line no-undef
self instanceof WorkerGlobalScope && typeof self.importScripts === "function";
})();
var origin = hasBrowserEnv && window.location.href || "http://localhost";
// node_modules/axios/lib/platform/index.js
var platform_default = {
...utils_exports,
...browser_default
};
// node_modules/axios/lib/helpers/toURLEncodedForm.js
function toURLEncodedForm(data, options) {
return toFormData_default(data, new platform_default.classes.URLSearchParams(), {
visitor: function(value, key, path, helpers) {
if (platform_default.isNode && utils_default.isBuffer(value)) {
this.append(key, value.toString("base64"));
return false;
}
return helpers.defaultVisitor.apply(this, arguments);
},
...options
});
}
// node_modules/axios/lib/helpers/formDataToJSON.js
function parsePropPath(name) {
return utils_default.matchAll(/\w+|\[(\w*)]/g, name).map((match) => {
return match[0] === "[]" ? "" : match[1] || match[0];
});
}
function arrayToObject(arr) {
const obj = {};
const keys = Object.keys(arr);
let i;
const len = keys.length;
let key;
for (i = 0; i < len; i++) {
key = keys[i];
obj[key] = arr[key];
}
return obj;
}
function formDataToJSON(formData) {
function buildPath(path, value, target, index) {
let name = path[index++];
if (name === "__proto__") return true;
const isNumericKey = Number.isFinite(+name);
const isLast = index >= path.length;
name = !name && utils_default.isArray(target) ? target.length : name;
if (isLast) {
if (utils_default.hasOwnProp(target, name)) {
target[name] = [target[name], value];
} else {
target[name] = value;
}
return !isNumericKey;
}
if (!target[name] || !utils_default.isObject(target[name])) {
target[name] = [];
}
const result = buildPath(path, value, target[name], index);
if (result && utils_default.isArray(target[name])) {
target[name] = arrayToObject(target[name]);
}
return !isNumericKey;
}
if (utils_default.isFormData(formData) && utils_default.isFunction(formData.entries)) {
const obj = {};
utils_default.forEachEntry(formData, (name, value) => {
buildPath(parsePropPath(name), value, obj, 0);
});
return obj;
}
return null;
}
var formDataToJSON_default = formDataToJSON;
// node_modules/axios/lib/defaults/index.js
function stringifySafely(rawValue, parser, encoder) {
if (utils_default.isString(rawValue)) {
try {
(parser || JSON.parse)(rawValue);
return utils_default.trim(rawValue);
} catch (e) {
if (e.name !== "SyntaxError") {
throw e;
}
}
}
return (encoder || JSON.stringify)(rawValue);
}
var defaults = {
transitional: transitional_default,
adapter: ["xhr", "http", "fetch"],
transformRequest: [function transformRequest(data, headers) {
const contentType = headers.getContentType() || "";
const hasJSONContentType = contentType.indexOf("application/json") > -1;
const isObjectPayload = utils_default.isObject(data);
if (isObjectPayload && utils_default.isHTMLForm(data)) {
data = new FormData(data);
}
const isFormData2 = utils_default.isFormData(data);
if (isFormData2) {
return hasJSONContentType ? JSON.stringify(formDataToJSON_default(data)) : data;
}
if (utils_default.isArrayBuffer(data) || utils_default.isBuffer(data) || utils_default.isStream(data) || utils_default.isFile(data) || utils_default.isBlob(data) || utils_default.isReadableStream(data)) {
return data;
}
if (utils_default.isArrayBufferView(data)) {
return data.buffer;
}
if (utils_default.isURLSearchParams(data)) {
headers.setContentType("application/x-www-form-urlencoded;charset=utf-8", false);
return data.toString();
}
let isFileList2;
if (isObjectPayload) {
if (contentType.indexOf("application/x-www-form-urlencoded") > -1) {
return toURLEncodedForm(data, this.formSerializer).toString();
}
if ((isFileList2 = utils_default.isFileList(data)) || contentType.indexOf("multipart/form-data") > -1) {
const _FormData = this.env && this.env.FormData;
return toFormData_default(
isFileList2 ? { "files[]": data } : data,
_FormData && new _FormData(),
this.formSerializer
);
}
}
if (isObjectPayload || hasJSONContentType) {
headers.setContentType("application/json", false);
return stringifySafely(data);
}
return data;
}],
transformResponse: [function transformResponse(data) {
const transitional2 = this.transitional || defaults.transitional;
const forcedJSONParsing = transitional2 && transitional2.forcedJSONParsing;
const JSONRequested = this.responseType === "json";
if (utils_default.isResponse(data) || utils_default.isReadableStream(data)) {
return data;
}
if (data && utils_default.isString(data) && (forcedJSONParsing && !this.responseType || JSONRequested)) {
const silentJSONParsing = transitional2 && transitional2.silentJSONParsing;
const strictJSONParsing = !silentJSONParsing && JSONRequested;
try {
return JSON.parse(data, this.parseReviver);
} catch (e) {
if (strictJSONParsing) {
if (e.name === "SyntaxError") {
throw AxiosError_default.from(e, AxiosError_default.ERR_BAD_RESPONSE, this, null, this.response);
}
throw e;
}
}
}
return data;
}],
/**
* A timeout in milliseconds to abort a request. If set to 0 (default) a
* timeout is not created.
*/
timeout: 0,
xsrfCookieName: "XSRF-TOKEN",
xsrfHeaderName: "X-XSRF-TOKEN",
maxContentLength: -1,
maxBodyLength: -1,
env: {
FormData: platform_default.classes.FormData,
Blob: platform_default.classes.Blob
},
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300;
},
headers: {
common: {
"Accept": "application/json, text/plain, */*",
"Content-Type": void 0
}
}
};
utils_default.forEach(["delete", "get", "head", "post", "put", "patch"], (method) => {
defaults.headers[method] = {};
});
var defaults_default = defaults;
// node_modules/axios/lib/helpers/parseHeaders.js
var ignoreDuplicateOf = utils_default.toObjectSet([
"age",
"authorization",
"content-length",
"content-type",
"etag",
"expires",
"from",
"host",
"if-modified-since",
"if-unmodified-since",
"last-modified",
"location",
"max-forwards",
"proxy-authorization",
"referer",
"retry-after",
"user-agent"
]);
var parseHeaders_default = (rawHeaders) => {
const parsed = {};
let key;
let val;
let i;
rawHeaders && rawHeaders.split("\n").forEach(function parser(line) {
i = line.indexOf(":");
key = line.substring(0, i).trim().toLowerCase();
val = line.substring(i + 1).trim();
if (!key || parsed[key] && ignoreDuplicateOf[key]) {
return;
}
if (key === "set-cookie") {
if (parsed[key]) {
parsed[key].push(val);
} else {
parsed[key] = [val];
}
} else {
parsed[key] = parsed[key] ? parsed[key] + ", " + val : val;
}
});
return parsed;
};
// node_modules/axios/lib/core/AxiosHeaders.js
var $internals = Symbol("internals");
function normalizeHeader(header) {
return header && String(header).trim().toLowerCase();
}
function normalizeValue(value) {
if (value === false || value == null) {
return value;
}
return utils_default.isArray(value) ? value.map(normalizeValue) : String(value);
}
function parseTokens(str) {
const tokens = /* @__PURE__ */ Object.create(null);
const tokensRE = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;
let match;
while (match = tokensRE.exec(str)) {
tokens[match[1]] = match[2];
}
return tokens;
}
var isValidHeaderName = (str) => /^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(str.trim());
function matchHeaderValue(context, value, header, filter2, isHeaderNameFilter) {
if (utils_default.isFunction(filter2)) {
return filter2.call(this, value, header);
}
if (isHeaderNameFilter) {
value = header;
}
if (!utils_default.isString(value)) return;
if (utils_default.isString(filter2)) {
return value.indexOf(filter2) !== -1;
}
if (utils_default.isRegExp(filter2)) {
return filter2.test(value);
}
}
function formatHeader(header) {
return header.trim().toLowerCase().replace(/([a-z\d])(\w*)/g, (w, char, str) => {
return char.toUpperCase() + str;
});
}
function buildAccessors(obj, header) {
const accessorName = utils_default.toCamelCase(" " + header);
["get", "set", "has"].forEach((methodName) => {
Object.defineProperty(obj, methodName + accessorName, {
value: function(arg1, arg2, arg3) {
return this[methodName].call(this, header, arg1, arg2, arg3);
},
configurable: true
});
});
}
var AxiosHeaders = class {
constructor(headers) {
headers && this.set(headers);
}
set(header, valueOrRewrite, rewrite) {
const self2 = this;
function setHeader(_value, _header, _rewrite) {
const lHeader = normalizeHeader(_header);
if (!lHeader) {
throw new Error("header name must be a non-empty string");
}
const key = utils_default.findKey(self2, lHeader);
if (!key || self2[key] === void 0 || _rewrite === true || _rewrite === void 0 && self2[key] !== false) {
self2[key || _header] = normalizeValue(_value);
}
}
const setHeaders = (headers, _rewrite) => utils_default.forEach(headers, (_value, _header) => setHeader(_value, _header, _rewrite));
if (utils_default.isPlainObject(header) || header instanceof this.constructor) {
setHeaders(header, valueOrRewrite);
} else if (utils_default.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) {
setHeaders(parseHeaders_default(header), valueOrRewrite);
} else if (utils_default.isObject(header) && utils_default.isIterable(header)) {
let obj = {}, dest, key;
for (const entry of header) {
if (!utils_default.isArray(entry)) {
throw TypeError("Object iterator must return a key-value pair");
}
obj[key = entry[0]] = (dest = obj[key]) ? utils_default.isArray(dest) ? [...dest, entry[1]] : [dest, entry[1]] : entry[1];
}
setHeaders(obj, valueOrRewrite);
} else {
header != null && setHeader(valueOrRewrite, header, rewrite);
}
return this;
}
get(header, parser) {
header = normalizeHeader(header);
if (header) {
const key = utils_default.findKey(this, header);
if (key) {
const value = this[key];
if (!parser) {
return value;
}
if (parser === true) {
return parseTokens(value);
}
if (utils_default.isFunction(parser)) {
return parser.call(this, value, key);
}
if (utils_default.isRegExp(parser)) {
return parser.exec(value);
}
throw new TypeError("parser must be boolean|regexp|function");
}
}
}
has(header, matcher) {
header = normalizeHeader(header);
if (header) {
const key = utils_default.findKey(this, header);
return !!(key && this[key] !== void 0 && (!matcher || matchHeaderValue(this, this[key], key, matcher)));
}
return false;
}
delete(header, matcher) {
const self2 = this;
let deleted = false;
function deleteHeader(_header) {
_header = normalizeHeader(_header);
if (_header) {
const key = utils_default.findKey(self2, _header);
if (key && (!matcher || matchHeaderValue(self2, self2[key], key, matcher))) {
delete self2[key];
deleted = true;
}
}
}
if (utils_default.isArray(header)) {
header.forEach(deleteHeader);
} else {
deleteHeader(header);
}
return deleted;
}
clear(matcher) {
const keys = Object.keys(this);
let i = keys.length;
let deleted = false;
while (i--) {
const key = keys[i];
if (!matcher || matchHeaderValue(this, this[key], key, matcher, true)) {
delete this[key];
deleted = true;
}
}
return deleted;
}
normalize(format) {
const self2 = this;
const headers = {};
utils_default.forEach(this, (value, header) => {
const key = utils_default.findKey(headers, header);
if (key) {
self2[key] = normalizeValue(value);
delete self2[header];
return;
}
const normalized = format ? formatHeader(header) : String(header).trim();
if (normalized !== header) {
delete self2[header];
}
self2[normalized] = normalizeValue(value);
headers[normalized] = true;
});
return this;
}
concat(...targets) {
return this.constructor.concat(this, ...targets);
}
toJSON(asStrings) {
const obj = /* @__PURE__ */ Object.create(null);
utils_default.forEach(this, (value, header) => {
value != null && value !== false && (obj[header] = asStrings && utils_default.isArray(value) ? value.join(", ") : value);
});
return obj;
}
[Symbol.iterator]() {
return Object.entries(this.toJSON())[Symbol.iterator]();
}
toString() {
return Object.entries(this.toJSON()).map(([header, value]) => header + ": " + value).join("\n");
}
getSetCookie() {
return this.get("set-cookie") || [];
}
get [Symbol.toStringTag]() {
return "AxiosHeaders";
}
static from(thing) {
return thing instanceof this ? thing : new this(thing);
}
static concat(first, ...targets) {
const computed = new this(first);
targets.forEach((target) => computed.set(target));
return computed;
}
static accessor(header) {
const internals = this[$internals] = this[$internals] = {
accessors: {}
};
const accessors = internals.accessors;
const prototype3 = this.prototype;
function defineAccessor(_header) {
const lHeader = normalizeHeader(_header);
if (!accessors[lHeader]) {
buildAccessors(prototype3, _header);
accessors[lHeader] = true;
}
}
utils_default.isArray(header) ? header.forEach(defineAccessor) : defineAccessor(header);
return this;
}
};
AxiosHeaders.accessor(["Content-Type", "Content-Length", "Accept", "Accept-Encoding", "User-Agent", "Authorization"]);
utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
let mapped = key[0].toUpperCase() + key.slice(1);
return {
get: () => value,
set(headerValue) {
this[mapped] = headerValue;
}
};
});
utils_default.freezeMethods(AxiosHeaders);
var AxiosHeaders_default = AxiosHeaders;
// node_modules/axios/lib/core/transformData.js
function transformData(fns, response) {
const config = this || defaults_default;
const context = response || config;
const headers = AxiosHeaders_default.from(context.headers);
let data = context.data;
utils_default.forEach(fns, function transform(fn) {
data = fn.call(config, data, headers.normalize(), response ? response.status : void 0);
});
headers.normalize();
return data;
}
// node_modules/axios/lib/cancel/isCancel.js
function isCancel(value) {
return !!(value && value.__CANCEL__);
}
// node_modules/axios/lib/cancel/CanceledError.js
function CanceledError(message, config, request) {
AxiosError_default.call(this, message == null ? "canceled" : message, AxiosError_default.ERR_CANCELED, config, request);
this.name = "CanceledError";
}
utils_default.inherits(CanceledError, AxiosError_default, {
__CANCEL__: true
});
var CanceledError_default = CanceledError;
// node_modules/axios/lib/core/settle.js
function settle(resolve, reject, response) {
const validateStatus2 = response.config.validateStatus;
if (!response.status || !validateStatus2 || validateStatus2(response.status)) {
resolve(response);
} else {
reject(new AxiosError_default(
"Request failed with status code " + response.status,
[AxiosError_default.ERR_BAD_REQUEST, AxiosError_default.ERR_BAD_RESPONSE][Math.floor(response.status / 100) - 4],
response.config,
response.request,
response
));
}
}
// node_modules/axios/lib/helpers/parseProtocol.js
function parseProtocol(url) {
const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url);
return match && match[1] || "";
}
// node_modules/axios/lib/helpers/speedometer.js
function speedometer(samplesCount, min) {
samplesCount = samplesCount || 10;
const bytes = new Array(samplesCount);
const timestamps = new Array(samplesCount);
let head = 0;
let tail = 0;
let firstSampleTS;
min = min !== void 0 ? min : 1e3;
return function push(chunkLength) {
const now = Date.now();
const startedAt = timestamps[tail];
if (!firstSampleTS) {
firstSampleTS = now;
}
bytes[head] = chunkLength;
timestamps[head] = now;
let i = tail;
let bytesCount = 0;
while (i !== head) {
bytesCount += bytes[i++];
i = i % samplesCount;
}
head = (head + 1) % samplesCount;
if (head === tail) {
tail = (tail + 1) % samplesCount;
}
if (now - firstSampleTS < min) {
return;
}
const passed = startedAt && now - startedAt;
return passed ? Math.round(bytesCount * 1e3 / passed) : void 0;
};
}
var speedometer_default = speedometer;
// node_modules/axios/lib/helpers/throttle.js
function throttle(fn, freq) {
let timestamp = 0;
let threshold = 1e3 / freq;
let lastArgs;
let timer;
const invoke = (args, now = Date.now()) => {
timestamp = now;
lastArgs = null;
if (timer) {
clearTimeout(timer);
timer = null;
}
fn(...args);
};
const throttled = (...args) => {
const now = Date.now();
const passed = now - timestamp;
if (passed >= threshold) {
invoke(args, now);
} else {
lastArgs = args;
if (!timer) {
timer = setTimeout(() => {
timer = null;
invoke(lastArgs);
}, threshold - passed);
}
}
};
const flush = () => lastArgs && invoke(lastArgs);
return [throttled, flush];
}
var throttle_default = throttle;
// node_modules/axios/lib/helpers/progressEventReducer.js
var progressEventReducer = (listener, isDownloadStream, freq = 3) => {
let bytesNotified = 0;
const _speedometer = speedometer_default(50, 250);
return throttle_default((e) => {
const loaded = e.loaded;
const total = e.lengthComputable ? e.total : void 0;
const progressBytes = loaded - bytesNotified;
const rate = _speedometer(progressBytes);
const inRange = loaded <= total;
bytesNotified = loaded;
const data = {
loaded,
total,
progress: total ? loaded / total : void 0,
bytes: progressBytes,
rate: rate ? rate : void 0,
estimated: rate && total && inRange ? (total - loaded) / rate : void 0,
event: e,
lengthComputable: total != null,
[isDownloadStream ? "download" : "upload"]: true
};
listener(data);
}, freq);
};
var progressEventDecorator = (total, throttled) => {
const lengthComputable = total != null;
return [(loaded) => throttled[0]({
lengthComputable,
total,
loaded
}), throttled[1]];
};
var asyncDecorator = (fn) => (...args) => utils_default.asap(() => fn(...args));
// node_modules/axios/lib/helpers/isURLSameOrigin.js
var isURLSameOrigin_default = platform_default.hasStandardBrowserEnv ? /* @__PURE__ */ ((origin2, isMSIE) => (url) => {
url = new URL(url, platform_default.origin);
return origin2.protocol === url.protocol && origin2.host === url.host && (isMSIE || origin2.port === url.port);
})(
new URL(platform_default.origin),
platform_default.navigator && /(msie|trident)/i.test(platform_default.navigator.userAgent)
) : () => true;
// node_modules/axios/lib/helpers/cookies.js
var cookies_default = platform_default.hasStandardBrowserEnv ? (
// Standard browser envs support document.cookie
{
write(name, value, expires, path, domain, secure, sameSite) {
if (typeof document === "undefined") return;
const cookie = [`${name}=${encodeURIComponent(value)}`];
if (utils_default.isNumber(expires)) {
cookie.push(`expires=${new Date(expires).toUTCString()}`);
}
if (utils_default.isString(path)) {
cookie.push(`path=${path}`);
}
if (utils_default.isString(domain)) {
cookie.push(`domain=${domain}`);
}
if (secure === true) {
cookie.push("secure");
}
if (utils_default.isString(sameSite)) {
cookie.push(`SameSite=${sameSite}`);
}
document.cookie = cookie.join("; ");
},
read(name) {
if (typeof document === "undefined") return null;
const match = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
return match ? decodeURIComponent(match[1]) : null;
},
remove(name) {
this.write(name, "", Date.now() - 864e5, "/");
}
}
) : (
// Non-standard browser env (web workers, react-native) lack needed support.
{
write() {
},
read() {
return null;
},
remove() {
}
}
);
// node_modules/axios/lib/helpers/isAbsoluteURL.js
function isAbsoluteURL(url) {
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
}
// node_modules/axios/lib/helpers/combineURLs.js
function combineURLs(baseURL, relativeURL) {
return relativeURL ? baseURL.replace(/\/?\/$/, "") + "/" + relativeURL.replace(/^\/+/, "") : baseURL;
}
// node_modules/axios/lib/core/buildFullPath.js
function buildFullPath(baseURL, requestedURL, allowAbsoluteUrls) {
let isRelativeUrl = !isAbsoluteURL(requestedURL);
if (baseURL && (isRelativeUrl || allowAbsoluteUrls == false)) {
return combineURLs(baseURL, requestedURL);
}
return requestedURL;
}
// node_modules/axios/lib/core/mergeConfig.js
var headersToObject = (thing) => thing instanceof AxiosHeaders_default ? { ...thing } : thing;
function mergeConfig(config1, config2) {
config2 = config2 || {};
const config = {};
function getMergedValue(target, source, prop, caseless) {
if (utils_default.isPlainObject(target) && utils_default.isPlainObject(source)) {
return utils_default.merge.call({ caseless }, target, source);
} else if (utils_default.isPlainObject(source)) {
return utils_default.merge({}, source);
} else if (utils_default.isArray(source)) {
return source.slice();
}
return source;
}
function mergeDeepProperties(a, b, prop, caseless) {
if (!utils_default.isUndefined(b)) {
return getMergedValue(a, b, prop, caseless);
} else if (!utils_default.isUndefined(a)) {
return getMergedValue(void 0, a, prop, caseless);
}
}
function valueFromConfig2(a, b) {
if (!utils_default.isUndefined(b)) {
return getMergedValue(void 0, b);
}
}
function defaultToConfig2(a, b) {
if (!utils_default.isUndefined(b)) {
return getMergedValue(void 0, b);
} else if (!utils_default.isUndefined(a)) {
return getMergedValue(void 0, a);
}
}
function mergeDirectKeys(a, b, prop) {
if (prop in config2) {
return getMergedValue(a, b);
} else if (prop in config1) {
return getMergedValue(void 0, a);
}
}
const mergeMap = {
url: valueFromConfig2,
method: valueFromConfig2,
data: valueFromConfig2,
baseURL: defaultToConfig2,
transformRequest: defaultToConfig2,
transformResponse: defaultToConfig2,
paramsSerializer: defaultToConfig2,
timeout: defaultToConfig2,
timeoutMessage: defaultToConfig2,
withCredentials: defaultToConfig2,
withXSRFToken: defaultToConfig2,
adapter: defaultToConfig2,
responseType: defaultToConfig2,
xsrfCookieName: defaultToConfig2,
xsrfHeaderName: defaultToConfig2,
onUploadProgress: defaultToConfig2,
onDownloadProgress: defaultToConfig2,
decompress: defaultToConfig2,
maxContentLength: defaultToConfig2,
maxBodyLength: defaultToConfig2,
beforeRedirect: defaultToConfig2,
transport: defaultToConfig2,
httpAgent: defaultToConfig2,
httpsAgent: defaultToConfig2,
cancelToken: defaultToConfig2,
socketPath: defaultToConfig2,
responseEncoding: defaultToConfig2,
validateStatus: mergeDirectKeys,
headers: (a, b, prop) => mergeDeepProperties(headersToObject(a), headersToObject(b), prop, true)
};
utils_default.forEach(Object.keys({ ...config1, ...config2 }), function computeConfigValue(prop) {
const merge2 = mergeMap[prop] || mergeDeepProperties;
const configValue = merge2(config1[prop], config2[prop], prop);
utils_default.isUndefined(configValue) && merge2 !== mergeDirectKeys || (config[prop] = configValue);
});
return config;
}
// node_modules/axios/lib/helpers/resolveConfig.js
var resolveConfig_default = (config) => {
const newConfig = mergeConfig({}, config);
let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
newConfig.headers = headers = AxiosHeaders_default.from(headers);
newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url, newConfig.allowAbsoluteUrls), config.params, config.paramsSerializer);
if (auth) {
headers.set(
"Authorization",
"Basic " + btoa((auth.username || "") + ":" + (auth.password ? unescape(encodeURIComponent(auth.password)) : ""))
);
}
if (utils_default.isFormData(data)) {
if (platform_default.hasStandardBrowserEnv || platform_default.hasStandardBrowserWebWorkerEnv) {
headers.setContentType(void 0);
} else if (utils_default.isFunction(data.getHeaders)) {
const formHeaders = data.getHeaders();
const allowedHeaders = ["content-type", "content-length"];
Object.entries(formHeaders).forEach(([key, val]) => {
if (allowedHeaders.includes(key.toLowerCase())) {
headers.set(key, val);
}
});
}
}
if (platform_default.hasStandardBrowserEnv) {
withXSRFToken && utils_default.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig));
if (withXSRFToken || withXSRFToken !== false && isURLSameOrigin_default(newConfig.url)) {
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies_default.read(xsrfCookieName);
if (xsrfValue) {
headers.set(xsrfHeaderName, xsrfValue);
}
}
}
return newConfig;
};
// node_modules/axios/lib/adapters/xhr.js
var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
var xhr_default = isXHRAdapterSupported && function(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
const _config = resolveConfig_default(config);
let requestData = _config.data;
const requestHeaders = AxiosHeaders_default.from(_config.headers).normalize();
let { responseType, onUploadProgress, onDownloadProgress } = _config;
let onCanceled;
let uploadThrottled, downloadThrottled;
let flushUpload, flushDownload;
function done() {
flushUpload && flushUpload();
flushDownload && flushDownload();
_config.cancelToken && _config.cancelToken.unsubscribe(onCanceled);
_config.signal && _config.signal.removeEventListener("abort", onCanceled);
}
let request = new XMLHttpRequest();
request.open(_config.method.toUpperCase(), _config.url, true);
request.timeout = _config.timeout;
function onloadend() {
if (!request) {
return;
}
const responseHeaders = AxiosHeaders_default.from(
"getAllResponseHeaders" in request && request.getAllResponseHeaders()
);
const responseData = !responseType || responseType === "text" || responseType === "json" ? request.responseText : request.response;
const response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config,
request
};
settle(function _resolve(value) {
resolve(value);
done();
}, function _reject(err) {
reject(err);
done();
}, response);
request = null;
}
if ("onloadend" in request) {
request.onloadend = onloadend;
} else {
request.onreadystatechange = function handleLoad() {
if (!request || request.readyState !== 4) {
return;
}
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf("file:") === 0)) {
return;
}
setTimeout(onloadend);
};
}
request.onabort = function handleAbort() {
if (!request) {
return;
}
reject(new AxiosError_default("Request aborted", AxiosError_default.ECONNABORTED, config, request));
request = null;
};
request.onerror = function handleError(event) {
const msg = event && event.message ? event.message : "Network Error";
const err = new AxiosError_default(msg, AxiosError_default.ERR_NETWORK, config, request);
err.event = event || null;
reject(err);
request = null;
};
request.ontimeout = function handleTimeout() {
let timeoutErrorMessage = _config.timeout ? "timeout of " + _config.timeout + "ms exceeded" : "timeout exceeded";
const transitional2 = _config.transitional || transitional_default;
if (_config.timeoutErrorMessage) {
timeoutErrorMessage = _config.timeoutErrorMessage;
}
reject(new AxiosError_default(
timeoutErrorMessage,
transitional2.clarifyTimeoutError ? AxiosError_default.ETIMEDOUT : AxiosError_default.ECONNABORTED,
config,
request
));
request = null;
};
requestData === void 0 && requestHeaders.setContentType(null);
if ("setRequestHeader" in request) {
utils_default.forEach(requestHeaders.toJSON(), function setRequestHeader(val, key) {
request.setRequestHeader(key, val);
});
}
if (!utils_default.isUndefined(_config.withCredentials)) {
request.withCredentials = !!_config.withCredentials;
}
if (responseType && responseType !== "json") {
request.responseType = _config.responseType;
}
if (onDownloadProgress) {
[downloadThrottled, flushDownload] = progressEventReducer(onDownloadProgress, true);
request.addEventListener("progress", downloadThrottled);
}
if (onUploadProgress && request.upload) {
[uploadThrottled, flushUpload] = progressEventReducer(onUploadProgress);
request.upload.addEventListener("progress", uploadThrottled);
request.upload.addEventListener("loadend", flushUpload);
}
if (_config.cancelToken || _config.signal) {
onCanceled = (cancel) => {
if (!request) {
return;
}
reject(!cancel || cancel.type ? new CanceledError_default(null, config, request) : cancel);
request.abort();
request = null;
};
_config.cancelToken && _config.cancelToken.subscribe(onCanceled);
if (_config.signal) {
_config.signal.aborted ? onCanceled() : _config.signal.addEventListener("abort", onCanceled);
}
}
const protocol = parseProtocol(_config.url);
if (protocol && platform_default.protocols.indexOf(protocol) === -1) {
reject(new AxiosError_default("Unsupported protocol " + protocol + ":", AxiosError_default.ERR_BAD_REQUEST, config));
return;
}
request.send(requestData || null);
});
};
// node_modules/axios/lib/helpers/composeSignals.js
var composeSignals = (signals, timeout) => {
const { length } = signals = signals ? signals.filter(Boolean) : [];
if (timeout || length) {
let controller = new AbortController();
let aborted;
const onabort = function(reason) {
if (!aborted) {
aborted = true;
unsubscribe();
const err = reason instanceof Error ? reason : this.reason;
controller.abort(err instanceof AxiosError_default ? err : new CanceledError_default(err instanceof Error ? err.message : err));
}
};
let timer = timeout && setTimeout(() => {
timer = null;
onabort(new AxiosError_default(`timeout ${timeout} of ms exceeded`, AxiosError_default.ETIMEDOUT));
}, timeout);
const unsubscribe = () => {
if (signals) {
timer && clearTimeout(timer);
timer = null;
signals.forEach((signal2) => {
signal2.unsubscribe ? signal2.unsubscribe(onabort) : signal2.removeEventListener("abort", onabort);
});
signals = null;
}
};
signals.forEach((signal2) => signal2.addEventListener("abort", onabort));
const { signal } = controller;
signal.unsubscribe = () => utils_default.asap(unsubscribe);
return signal;
}
};
var composeSignals_default = composeSignals;
// node_modules/axios/lib/helpers/trackStream.js
var streamChunk = function* (chunk, chunkSize) {
let len = chunk.byteLength;
if (!chunkSize || len < chunkSize) {
yield chunk;
return;
}
let pos = 0;
let end;
while (pos < len) {
end = pos + chunkSize;
yield chunk.slice(pos, end);
pos = end;
}
};
var readBytes = async function* (iterable, chunkSize) {
for await (const chunk of readStream(iterable)) {
yield* streamChunk(chunk, chunkSize);
}
};
var readStream = async function* (stream) {
if (stream[Symbol.asyncIterator]) {
yield* stream;
return;
}
const reader = stream.getReader();
try {
for (; ; ) {
const { done, value } = await reader.read();
if (done) {
break;
}
yield value;
}
} finally {
await reader.cancel();
}
};
var trackStream = (stream, chunkSize, onProgress, onFinish) => {
const iterator2 = readBytes(stream, chunkSize);
let bytes = 0;
let done;
let _onFinish = (e) => {
if (!done) {
done = true;
onFinish && onFinish(e);
}
};
return new ReadableStream({
async pull(controller) {
try {
const { done: done2, value } = await iterator2.next();
if (done2) {
_onFinish();
controller.close();
return;
}
let len = value.byteLength;
if (onProgress) {
let loadedBytes = bytes += len;
onProgress(loadedBytes);
}
controller.enqueue(new Uint8Array(value));
} catch (err) {
_onFinish(err);
throw err;
}
},
cancel(reason) {
_onFinish(reason);
return iterator2.return();
}
}, {
highWaterMark: 2
});
};
// node_modules/axios/lib/adapters/fetch.js
var DEFAULT_CHUNK_SIZE = 64 * 1024;
var { isFunction: isFunction2 } = utils_default;
var globalFetchAPI = (({ Request, Response }) => ({
Request,
Response
}))(utils_default.global);
var {
ReadableStream: ReadableStream2,
TextEncoder
} = utils_default.global;
var test = (fn, ...args) => {
try {
return !!fn(...args);
} catch (e) {
return false;
}
};
var factory = (env) => {
env = utils_default.merge.call({
skipUndefined: true
}, globalFetchAPI, env);
const { fetch: envFetch, Request, Response } = env;
const isFetchSupported = envFetch ? isFunction2(envFetch) : typeof fetch === "function";
const isRequestSupported = isFunction2(Request);
const isResponseSupported = isFunction2(Response);
if (!isFetchSupported) {
return false;
}
const isReadableStreamSupported = isFetchSupported && isFunction2(ReadableStream2);
const encodeText = isFetchSupported && (typeof TextEncoder === "function" ? /* @__PURE__ */ ((encoder) => (str) => encoder.encode(str))(new TextEncoder()) : async (str) => new Uint8Array(await new Request(str).arrayBuffer()));
const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => {
let duplexAccessed = false;
const hasContentType = new Request(platform_default.origin, {
body: new ReadableStream2(),
method: "POST",
get duplex() {
duplexAccessed = true;
return "half";
}
}).headers.has("Content-Type");
return duplexAccessed && !hasContentType;
});
const supportsResponseStream = isResponseSupported && isReadableStreamSupported && test(() => utils_default.isReadableStream(new Response("").body));
const resolvers = {
stream: supportsResponseStream && ((res) => res.body)
};
isFetchSupported && (() => {
["text", "arrayBuffer", "blob", "formData", "stream"].forEach((type) => {
!resolvers[type] && (resolvers[type] = (res, config) => {
let method = res && res[type];
if (method) {
return method.call(res);
}
throw new AxiosError_default(`Response type '${type}' is not supported`, AxiosError_default.ERR_NOT_SUPPORT, config);
});
});
})();
const getBodyLength = async (body) => {
if (body == null) {
return 0;
}
if (utils_default.isBlob(body)) {
return body.size;
}
if (utils_default.isSpecCompliantForm(body)) {
const _request = new Request(platform_default.origin, {
method: "POST",
body
});
return (await _request.arrayBuffer()).byteLength;
}
if (utils_default.isArrayBufferView(body) || utils_default.isArrayBuffer(body)) {
return body.byteLength;
}
if (utils_default.isURLSearchParams(body)) {
body = body + "";
}
if (utils_default.isString(body)) {
return (await encodeText(body)).byteLength;
}
};
const resolveBodyLength = async (headers, body) => {
const length = utils_default.toFiniteNumber(headers.getContentLength());
return length == null ? getBodyLength(body) : length;
};
return async (config) => {
let {
url,
method,
data,
signal,
cancelToken,
timeout,
onDownloadProgress,
onUploadProgress,
responseType,
headers,
withCredentials = "same-origin",
fetchOptions
} = resolveConfig_default(config);
let _fetch = envFetch || fetch;
responseType = responseType ? (responseType + "").toLowerCase() : "text";
let composedSignal = composeSignals_default([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
let request = null;
const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
composedSignal.unsubscribe();
});
let requestContentLength;
try {
if (onUploadProgress && supportsRequestStream && method !== "get" && method !== "head" && (requestContentLength = await resolveBodyLength(headers, data)) !== 0) {
let _request = new Request(url, {
method: "POST",
body: data,
duplex: "half"
});
let contentTypeHeader;
if (utils_default.isFormData(data) && (contentTypeHeader = _request.headers.get("content-type"))) {
headers.setContentType(contentTypeHeader);
}
if (_request.body) {
const [onProgress, flush] = progressEventDecorator(
requestContentLength,
progressEventReducer(asyncDecorator(onUploadProgress))
);
data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
}
}
if (!utils_default.isString(withCredentials)) {
withCredentials = withCredentials ? "include" : "omit";
}
const isCredentialsSupported = isRequestSupported && "credentials" in Request.prototype;
const resolvedOptions = {
...fetchOptions,
signal: composedSignal,
method: method.toUpperCase(),
headers: headers.normalize().toJSON(),
body: data,
duplex: "half",
credentials: isCredentialsSupported ? withCredentials : void 0
};
request = isRequestSupported && new Request(url, resolvedOptions);
let response = await (isRequestSupported ? _fetch(request, fetchOptions) : _fetch(url, resolvedOptions));
const isStreamResponse = supportsResponseStream && (responseType === "stream" || responseType === "response");
if (supportsResponseStream && (onDownloadProgress || isStreamResponse && unsubscribe)) {
const options = {};
["status", "statusText", "headers"].forEach((prop) => {
options[prop] = response[prop];
});
const responseContentLength = utils_default.toFiniteNumber(response.headers.get("content-length"));
const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
responseContentLength,
progressEventReducer(asyncDecorator(onDownloadProgress), true)
) || [];
response = new Response(
trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
flush && flush();
unsubscribe && unsubscribe();
}),
options
);
}
responseType = responseType || "text";
let responseData = await resolvers[utils_default.findKey(resolvers, responseType) || "text"](response, config);
!isStreamResponse && unsubscribe && unsubscribe();
return await new Promise((resolve, reject) => {
settle(resolve, reject, {
data: responseData,
headers: AxiosHeaders_default.from(response.headers),
status: response.status,
statusText: response.statusText,
config,
request
});
});
} catch (err) {
unsubscribe && unsubscribe();
if (err && err.name === "TypeError" && /Load failed|fetch/i.test(err.message)) {
throw Object.assign(
new AxiosError_default("Network Error", AxiosError_default.ERR_NETWORK, config, request),
{
cause: err.cause || err
}
);
}
throw AxiosError_default.from(err, err && err.code, config, request);
}
};
};
var seedCache = /* @__PURE__ */ new Map();
var getFetch = (config) => {
let env = config && config.env || {};
const { fetch: fetch2, Request, Response } = env;
const seeds = [
Request,
Response,
fetch2
];
let len = seeds.length, i = len, seed, target, map = seedCache;
while (i--) {
seed = seeds[i];
target = map.get(seed);
target === void 0 && map.set(seed, target = i ? /* @__PURE__ */ new Map() : factory(env));
map = target;
}
return target;
};
var adapter = getFetch();
// node_modules/axios/lib/adapters/adapters.js
var knownAdapters = {
http: null_default,
xhr: xhr_default,
fetch: {
get: getFetch
}
};
utils_default.forEach(knownAdapters, (fn, value) => {
if (fn) {
try {
Object.defineProperty(fn, "name", { value });
} catch (e) {
}
Object.defineProperty(fn, "adapterName", { value });
}
});
var renderReason = (reason) => `- ${reason}`;
var isResolvedHandle = (adapter2) => utils_default.isFunction(adapter2) || adapter2 === null || adapter2 === false;
function getAdapter(adapters, config) {
adapters = utils_default.isArray(adapters) ? adapters : [adapters];
const { length } = adapters;
let nameOrAdapter;
let adapter2;
const rejectedReasons = {};
for (let i = 0; i < length; i++) {
nameOrAdapter = adapters[i];
let id;
adapter2 = nameOrAdapter;
if (!isResolvedHandle(nameOrAdapter)) {
adapter2 = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
if (adapter2 === void 0) {
throw new AxiosError_default(`Unknown adapter '${id}'`);
}
}
if (adapter2 && (utils_default.isFunction(adapter2) || (adapter2 = adapter2.get(config)))) {
break;
}
rejectedReasons[id || "#" + i] = adapter2;
}
if (!adapter2) {
const reasons = Object.entries(rejectedReasons).map(
([id, state]) => `adapter ${id} ` + (state === false ? "is not supported by the environment" : "is not available in the build")
);
let s = length ? reasons.length > 1 ? "since :\n" + reasons.map(renderReason).join("\n") : " " + renderReason(reasons[0]) : "as no adapter specified";
throw new AxiosError_default(
`There is no suitable adapter to dispatch the request ` + s,
"ERR_NOT_SUPPORT"
);
}
return adapter2;
}
var adapters_default = {
/**
* Resolve an adapter from a list of adapter names or functions.
* @type {Function}
*/
getAdapter,
/**
* Exposes all known adapters
* @type {Object<string, Function|Object>}
*/
adapters: knownAdapters
};
// node_modules/axios/lib/core/dispatchRequest.js
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
if (config.signal && config.signal.aborted) {
throw new CanceledError_default(null, config);
}
}
function dispatchRequest(config) {
throwIfCancellationRequested(config);
config.headers = AxiosHeaders_default.from(config.headers);
config.data = transformData.call(
config,
config.transformRequest
);
if (["post", "put", "patch"].indexOf(config.method) !== -1) {
config.headers.setContentType("application/x-www-form-urlencoded", false);
}
const adapter2 = adapters_default.getAdapter(config.adapter || defaults_default.adapter, config);
return adapter2(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
response.data = transformData.call(
config,
config.transformResponse,
response
);
response.headers = AxiosHeaders_default.from(response.headers);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
if (reason && reason.response) {
reason.response.data = transformData.call(
config,
config.transformResponse,
reason.response
);
reason.response.headers = AxiosHeaders_default.from(reason.response.headers);
}
}
return Promise.reject(reason);
});
}
// node_modules/axios/lib/env/data.js
var VERSION = "1.13.2";
// node_modules/axios/lib/helpers/validator.js
var validators = {};
["object", "boolean", "number", "function", "string", "symbol"].forEach((type, i) => {
validators[type] = function validator(thing) {
return typeof thing === type || "a" + (i < 1 ? "n " : " ") + type;
};
});
var deprecatedWarnings = {};
validators.transitional = function transitional(validator, version, message) {
function formatMessage(opt, desc) {
return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
}
return (value, opt, opts) => {
if (validator === false) {
throw new AxiosError_default(
formatMessage(opt, " has been removed" + (version ? " in " + version : "")),
AxiosError_default.ERR_DEPRECATED
);
}
if (version && !deprecatedWarnings[opt]) {
deprecatedWarnings[opt] = true;
console.warn(
formatMessage(
opt,
" has been deprecated since v" + version + " and will be removed in the near future"
)
);
}
return validator ? validator(value, opt, opts) : true;
};
};
validators.spelling = function spelling(correctSpelling) {
return (value, opt) => {
console.warn(`${opt} is likely a misspelling of ${correctSpelling}`);
return true;
};
};
function assertOptions(options, schema, allowUnknown) {
if (typeof options !== "object") {
throw new AxiosError_default("options must be an object", AxiosError_default.ERR_BAD_OPTION_VALUE);
}
const keys = Object.keys(options);
let i = keys.length;
while (i-- > 0) {
const opt = keys[i];
const validator = schema[opt];
if (validator) {
const value = options[opt];
const result = value === void 0 || validator(value, opt, options);
if (result !== true) {
throw new AxiosError_default("option " + opt + " must be " + result, AxiosError_default.ERR_BAD_OPTION_VALUE);
}
continue;
}
if (allowUnknown !== true) {
throw new AxiosError_default("Unknown option " + opt, AxiosError_default.ERR_BAD_OPTION);
}
}
}
var validator_default = {
assertOptions,
validators
};
// node_modules/axios/lib/core/Axios.js
var validators2 = validator_default.validators;
var Axios = class {
constructor(instanceConfig) {
this.defaults = instanceConfig || {};
this.interceptors = {
request: new InterceptorManager_default(),
response: new InterceptorManager_default()
};
}
/**
* Dispatch a request
*
* @param {String|Object} configOrUrl The config specific for this request (merged with this.defaults)
* @param {?Object} config
*
* @returns {Promise} The Promise to be fulfilled
*/
async request(configOrUrl, config) {
try {
return await this._request(configOrUrl, config);
} catch (err) {
if (err instanceof Error) {
let dummy = {};
Error.captureStackTrace ? Error.captureStackTrace(dummy) : dummy = new Error();
const stack = dummy.stack ? dummy.stack.replace(/^.+\n/, "") : "";
try {
if (!err.stack) {
err.stack = stack;
} else if (stack && !String(err.stack).endsWith(stack.replace(/^.+\n.+\n/, ""))) {
err.stack += "\n" + stack;
}
} catch (e) {
}
}
throw err;
}
}
_request(configOrUrl, config) {
if (typeof configOrUrl === "string") {
config = config || {};
config.url = configOrUrl;
} else {
config = configOrUrl || {};
}
config = mergeConfig(this.defaults, config);
const { transitional: transitional2, paramsSerializer, headers } = config;
if (transitional2 !== void 0) {
validator_default.assertOptions(transitional2, {
silentJSONParsing: validators2.transitional(validators2.boolean),
forcedJSONParsing: validators2.transitional(validators2.boolean),
clarifyTimeoutError: validators2.transitional(validators2.boolean)
}, false);
}
if (paramsSerializer != null) {
if (utils_default.isFunction(paramsSerializer)) {
config.paramsSerializer = {
serialize: paramsSerializer
};
} else {
validator_default.assertOptions(paramsSerializer, {
encode: validators2.function,
serialize: validators2.function
}, true);
}
}
if (config.allowAbsoluteUrls !== void 0) {
} else if (this.defaults.allowAbsoluteUrls !== void 0) {
config.allowAbsoluteUrls = this.defaults.allowAbsoluteUrls;
} else {
config.allowAbsoluteUrls = true;
}
validator_default.assertOptions(config, {
baseUrl: validators2.spelling("baseURL"),
withXsrfToken: validators2.spelling("withXSRFToken")
}, true);
config.method = (config.method || this.defaults.method || "get").toLowerCase();
let contextHeaders = headers && utils_default.merge(
headers.common,
headers[config.method]
);
headers && utils_default.forEach(
["delete", "get", "head", "post", "put", "patch", "common"],
(method) => {
delete headers[method];
}
);
config.headers = AxiosHeaders_default.concat(contextHeaders, headers);
const requestInterceptorChain = [];
let synchronousRequestInterceptors = true;
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
if (typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false) {
return;
}
synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
});
const responseInterceptorChain = [];
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
});
let promise;
let i = 0;
let len;
if (!synchronousRequestInterceptors) {
const chain = [dispatchRequest.bind(this), void 0];
chain.unshift(...requestInterceptorChain);
chain.push(...responseInterceptorChain);
len = chain.length;
promise = Promise.resolve(config);
while (i < len) {
promise = promise.then(chain[i++], chain[i++]);
}
return promise;
}
len = requestInterceptorChain.length;
let newConfig = config;
while (i < len) {
const onFulfilled = requestInterceptorChain[i++];
const onRejected = requestInterceptorChain[i++];
try {
newConfig = onFulfilled(newConfig);
} catch (error) {
onRejected.call(this, error);
break;
}
}
try {
promise = dispatchRequest.call(this, newConfig);
} catch (error) {
return Promise.reject(error);
}
i = 0;
len = responseInterceptorChain.length;
while (i < len) {
promise = promise.then(responseInterceptorChain[i++], responseInterceptorChain[i++]);
}
return promise;
}
getUri(config) {
config = mergeConfig(this.defaults, config);
const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
return buildURL(fullPath, config.params, config.paramsSerializer);
}
};
utils_default.forEach(["delete", "get", "head", "options"], function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method,
url,
data: (config || {}).data
}));
};
});
utils_default.forEach(["post", "put", "patch"], function forEachMethodWithData(method) {
function generateHTTPMethod(isForm) {
return function httpMethod(url, data, config) {
return this.request(mergeConfig(config || {}, {
method,
headers: isForm ? {
"Content-Type": "multipart/form-data"
} : {},
url,
data
}));
};
}
Axios.prototype[method] = generateHTTPMethod();
Axios.prototype[method + "Form"] = generateHTTPMethod(true);
});
var Axios_default = Axios;
// node_modules/axios/lib/cancel/CancelToken.js
var CancelToken = class _CancelToken {
constructor(executor) {
if (typeof executor !== "function") {
throw new TypeError("executor must be a function.");
}
let resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
const token = this;
this.promise.then((cancel) => {
if (!token._listeners) return;
let i = token._listeners.length;
while (i-- > 0) {
token._listeners[i](cancel);
}
token._listeners = null;
});
this.promise.then = (onfulfilled) => {
let _resolve;
const promise = new Promise((resolve) => {
token.subscribe(resolve);
_resolve = resolve;
}).then(onfulfilled);
promise.cancel = function reject() {
token.unsubscribe(_resolve);
};
return promise;
};
executor(function cancel(message, config, request) {
if (token.reason) {
return;
}
token.reason = new CanceledError_default(message, config, request);
resolvePromise(token.reason);
});
}
/**
* Throws a `CanceledError` if cancellation has been requested.
*/
throwIfRequested() {
if (this.reason) {
throw this.reason;
}
}
/**
* Subscribe to the cancel signal
*/
subscribe(listener) {
if (this.reason) {
listener(this.reason);
return;
}
if (this._listeners) {
this._listeners.push(listener);
} else {
this._listeners = [listener];
}
}
/**
* Unsubscribe from the cancel signal
*/
unsubscribe(listener) {
if (!this._listeners) {
return;
}
const index = this._listeners.indexOf(listener);
if (index !== -1) {
this._listeners.splice(index, 1);
}
}
toAbortSignal() {
const controller = new AbortController();
const abort = (err) => {
controller.abort(err);
};
this.subscribe(abort);
controller.signal.unsubscribe = () => this.unsubscribe(abort);
return controller.signal;
}
/**
* Returns an object that contains a new `CancelToken` and a function that, when called,
* cancels the `CancelToken`.
*/
static source() {
let cancel;
const token = new _CancelToken(function executor(c) {
cancel = c;
});
return {
token,
cancel
};
}
};
var CancelToken_default = CancelToken;
// node_modules/axios/lib/helpers/spread.js
function spread(callback) {
return function wrap(arr) {
return callback.apply(null, arr);
};
}
// node_modules/axios/lib/helpers/isAxiosError.js
function isAxiosError(payload) {
return utils_default.isObject(payload) && payload.isAxiosError === true;
}
// node_modules/axios/lib/helpers/HttpStatusCode.js
var HttpStatusCode = {
Continue: 100,
SwitchingProtocols: 101,
Processing: 102,
EarlyHints: 103,
Ok: 200,
Created: 201,
Accepted: 202,
NonAuthoritativeInformation: 203,
NoContent: 204,
ResetContent: 205,
PartialContent: 206,
MultiStatus: 207,
AlreadyReported: 208,
ImUsed: 226,
MultipleChoices: 300,
MovedPermanently: 301,
Found: 302,
SeeOther: 303,
NotModified: 304,
UseProxy: 305,
Unused: 306,
TemporaryRedirect: 307,
PermanentRedirect: 308,
BadRequest: 400,
Unauthorized: 401,
PaymentRequired: 402,
Forbidden: 403,
NotFound: 404,
MethodNotAllowed: 405,
NotAcceptable: 406,
ProxyAuthenticationRequired: 407,
RequestTimeout: 408,
Conflict: 409,
Gone: 410,
LengthRequired: 411,
PreconditionFailed: 412,
PayloadTooLarge: 413,
UriTooLong: 414,
UnsupportedMediaType: 415,
RangeNotSatisfiable: 416,
ExpectationFailed: 417,
ImATeapot: 418,
MisdirectedRequest: 421,
UnprocessableEntity: 422,
Locked: 423,
FailedDependency: 424,
TooEarly: 425,
UpgradeRequired: 426,
PreconditionRequired: 428,
TooManyRequests: 429,
RequestHeaderFieldsTooLarge: 431,
UnavailableForLegalReasons: 451,
InternalServerError: 500,
NotImplemented: 501,
BadGateway: 502,
ServiceUnavailable: 503,
GatewayTimeout: 504,
HttpVersionNotSupported: 505,
VariantAlsoNegotiates: 506,
InsufficientStorage: 507,
LoopDetected: 508,
NotExtended: 510,
NetworkAuthenticationRequired: 511,
WebServerIsDown: 521,
ConnectionTimedOut: 522,
OriginIsUnreachable: 523,
TimeoutOccurred: 524,
SslHandshakeFailed: 525,
InvalidSslCertificate: 526
};
Object.entries(HttpStatusCode).forEach(([key, value]) => {
HttpStatusCode[value] = key;
});
var HttpStatusCode_default = HttpStatusCode;
// node_modules/axios/lib/axios.js
function createInstance(defaultConfig) {
const context = new Axios_default(defaultConfig);
const instance = bind(Axios_default.prototype.request, context);
utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true });
utils_default.extend(instance, context, null, { allOwnKeys: true });
instance.create = function create(instanceConfig) {
return createInstance(mergeConfig(defaultConfig, instanceConfig));
};
return instance;
}
var axios = createInstance(defaults_default);
axios.Axios = Axios_default;
axios.CanceledError = CanceledError_default;
axios.CancelToken = CancelToken_default;
axios.isCancel = isCancel;
axios.VERSION = VERSION;
axios.toFormData = toFormData_default;
axios.AxiosError = AxiosError_default;
axios.Cancel = axios.CanceledError;
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = spread;
axios.isAxiosError = isAxiosError;
axios.mergeConfig = mergeConfig;
axios.AxiosHeaders = AxiosHeaders_default;
axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing);
axios.getAdapter = adapters_default.getAdapter;
axios.HttpStatusCode = HttpStatusCode_default;
axios.default = axios;
var axios_default = axios;
// node_modules/axios/index.js
var {
Axios: Axios2,
AxiosError: AxiosError2,
CanceledError: CanceledError2,
isCancel: isCancel2,
CancelToken: CancelToken2,
VERSION: VERSION2,
all: all2,
Cancel,
isAxiosError: isAxiosError2,
spread: spread2,
toFormData: toFormData2,
AxiosHeaders: AxiosHeaders2,
HttpStatusCode: HttpStatusCode2,
formToJSON,
getAdapter: getAdapter2,
mergeConfig: mergeConfig2
} = axios_default;
export {
Axios2 as Axios,
AxiosError2 as AxiosError,
AxiosHeaders2 as AxiosHeaders,
Cancel,
CancelToken2 as CancelToken,
CanceledError2 as CanceledError,
HttpStatusCode2 as HttpStatusCode,
VERSION2 as VERSION,
all2 as all,
axios_default as default,
formToJSON,
getAdapter2 as getAdapter,
isAxiosError2 as isAxiosError,
isCancel2 as isCancel,
mergeConfig2 as mergeConfig,
spread2 as spread,
toFormData2 as toFormData
};
//# sourceMappingURL=axios.js.map
This diff could not be displayed because it is too large.
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
export {
__commonJS,
__export,
__toESM
};
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
import {
computed,
defineComponent,
getCurrentInstance,
h,
inject,
nextTick,
onActivated,
onDeactivated,
onUnmounted,
provide,
reactive,
ref,
shallowReactive,
shallowRef,
unref,
watch,
watchEffect
} from "./chunk-YHMWYXEE.js";
import "./chunk-G3PMV62Z.js";
// node_modules/vue-router/node_modules/@vue/devtools-api/lib/esm/env.js
function getDevtoolsGlobalHook() {
return getTarget().__VUE_DEVTOOLS_GLOBAL_HOOK__;
}
function getTarget() {
return typeof navigator !== "undefined" && typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : {};
}
var isProxyAvailable = typeof Proxy === "function";
// node_modules/vue-router/node_modules/@vue/devtools-api/lib/esm/const.js
var HOOK_SETUP = "devtools-plugin:setup";
var HOOK_PLUGIN_SETTINGS_SET = "plugin:settings:set";
// node_modules/vue-router/node_modules/@vue/devtools-api/lib/esm/time.js
var supported;
var perf;
function isPerformanceSupported() {
var _a;
if (supported !== void 0) {
return supported;
}
if (typeof window !== "undefined" && window.performance) {
supported = true;
perf = window.performance;
} else if (typeof globalThis !== "undefined" && ((_a = globalThis.perf_hooks) === null || _a === void 0 ? void 0 : _a.performance)) {
supported = true;
perf = globalThis.perf_hooks.performance;
} else {
supported = false;
}
return supported;
}
function now() {
return isPerformanceSupported() ? perf.now() : Date.now();
}
// node_modules/vue-router/node_modules/@vue/devtools-api/lib/esm/proxy.js
var ApiProxy = class {
constructor(plugin, hook) {
this.target = null;
this.targetQueue = [];
this.onQueue = [];
this.plugin = plugin;
this.hook = hook;
const defaultSettings = {};
if (plugin.settings) {
for (const id in plugin.settings) {
const item = plugin.settings[id];
defaultSettings[id] = item.defaultValue;
}
}
const localSettingsSaveId = `__vue-devtools-plugin-settings__${plugin.id}`;
let currentSettings = Object.assign({}, defaultSettings);
try {
const raw = localStorage.getItem(localSettingsSaveId);
const data = JSON.parse(raw);
Object.assign(currentSettings, data);
} catch (e) {
}
this.fallbacks = {
getSettings() {
return currentSettings;
},
setSettings(value) {
try {
localStorage.setItem(localSettingsSaveId, JSON.stringify(value));
} catch (e) {
}
currentSettings = value;
},
now() {
return now();
}
};
if (hook) {
hook.on(HOOK_PLUGIN_SETTINGS_SET, (pluginId, value) => {
if (pluginId === this.plugin.id) {
this.fallbacks.setSettings(value);
}
});
}
this.proxiedOn = new Proxy({}, {
get: (_target, prop) => {
if (this.target) {
return this.target.on[prop];
} else {
return (...args) => {
this.onQueue.push({
method: prop,
args
});
};
}
}
});
this.proxiedTarget = new Proxy({}, {
get: (_target, prop) => {
if (this.target) {
return this.target[prop];
} else if (prop === "on") {
return this.proxiedOn;
} else if (Object.keys(this.fallbacks).includes(prop)) {
return (...args) => {
this.targetQueue.push({
method: prop,
args,
resolve: () => {
}
});
return this.fallbacks[prop](...args);
};
} else {
return (...args) => {
return new Promise((resolve) => {
this.targetQueue.push({
method: prop,
args,
resolve
});
});
};
}
}
});
}
async setRealTarget(target) {
this.target = target;
for (const item of this.onQueue) {
this.target.on[item.method](...item.args);
}
for (const item of this.targetQueue) {
item.resolve(await this.target[item.method](...item.args));
}
}
};
// node_modules/vue-router/node_modules/@vue/devtools-api/lib/esm/index.js
function setupDevtoolsPlugin(pluginDescriptor, setupFn) {
const descriptor = pluginDescriptor;
const target = getTarget();
const hook = getDevtoolsGlobalHook();
const enableProxy = isProxyAvailable && descriptor.enableEarlyProxy;
if (hook && (target.__VUE_DEVTOOLS_PLUGIN_API_AVAILABLE__ || !enableProxy)) {
hook.emit(HOOK_SETUP, pluginDescriptor, setupFn);
} else {
const proxy = enableProxy ? new ApiProxy(descriptor, hook) : null;
const list = target.__VUE_DEVTOOLS_PLUGINS__ = target.__VUE_DEVTOOLS_PLUGINS__ || [];
list.push({
pluginDescriptor: descriptor,
setupFn,
proxy
});
if (proxy) {
setupFn(proxy.proxiedTarget);
}
}
}
// node_modules/vue-router/dist/devtools-BLCumUwL.mjs
var isBrowser = typeof document !== "undefined";
function isRouteComponent(component) {
return typeof component === "object" || "displayName" in component || "props" in component || "__vccOpts" in component;
}
function isESModule(obj) {
return obj.__esModule || obj[Symbol.toStringTag] === "Module" || obj.default && isRouteComponent(obj.default);
}
var assign = Object.assign;
function applyToParams(fn, params) {
const newParams = {};
for (const key in params) {
const value = params[key];
newParams[key] = isArray(value) ? value.map(fn) : fn(value);
}
return newParams;
}
var noop = () => {
};
var isArray = Array.isArray;
function mergeOptions(defaults, partialOptions) {
const options = {};
for (const key in defaults) options[key] = key in partialOptions ? partialOptions[key] : defaults[key];
return options;
}
function warn$1(msg) {
const args = Array.from(arguments).slice(1);
console.warn.apply(console, ["[Vue Router warn]: " + msg].concat(args));
}
var HASH_RE = /#/g;
var AMPERSAND_RE = /&/g;
var SLASH_RE = /\//g;
var EQUAL_RE = /=/g;
var IM_RE = /\?/g;
var PLUS_RE = /\+/g;
var ENC_BRACKET_OPEN_RE = /%5B/g;
var ENC_BRACKET_CLOSE_RE = /%5D/g;
var ENC_CARET_RE = /%5E/g;
var ENC_BACKTICK_RE = /%60/g;
var ENC_CURLY_OPEN_RE = /%7B/g;
var ENC_PIPE_RE = /%7C/g;
var ENC_CURLY_CLOSE_RE = /%7D/g;
var ENC_SPACE_RE = /%20/g;
function commonEncode(text) {
return text == null ? "" : encodeURI("" + text).replace(ENC_PIPE_RE, "|").replace(ENC_BRACKET_OPEN_RE, "[").replace(ENC_BRACKET_CLOSE_RE, "]");
}
function encodeHash(text) {
return commonEncode(text).replace(ENC_CURLY_OPEN_RE, "{").replace(ENC_CURLY_CLOSE_RE, "}").replace(ENC_CARET_RE, "^");
}
function encodeQueryValue(text) {
return commonEncode(text).replace(PLUS_RE, "%2B").replace(ENC_SPACE_RE, "+").replace(HASH_RE, "%23").replace(AMPERSAND_RE, "%26").replace(ENC_BACKTICK_RE, "`").replace(ENC_CURLY_OPEN_RE, "{").replace(ENC_CURLY_CLOSE_RE, "}").replace(ENC_CARET_RE, "^");
}
function encodeQueryKey(text) {
return encodeQueryValue(text).replace(EQUAL_RE, "%3D");
}
function encodePath(text) {
return commonEncode(text).replace(HASH_RE, "%23").replace(IM_RE, "%3F");
}
function encodeParam(text) {
return encodePath(text).replace(SLASH_RE, "%2F");
}
function decode(text) {
if (text == null) return null;
try {
return decodeURIComponent("" + text);
} catch (err) {
warn$1(`Error decoding "${text}". Using original value`);
}
return "" + text;
}
var TRAILING_SLASH_RE = /\/$/;
var removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, "");
function parseURL(parseQuery$1, location2, currentLocation = "/") {
let path, query = {}, searchString = "", hash = "";
const hashPos = location2.indexOf("#");
let searchPos = location2.indexOf("?");
searchPos = hashPos >= 0 && searchPos > hashPos ? -1 : searchPos;
if (searchPos >= 0) {
path = location2.slice(0, searchPos);
searchString = location2.slice(searchPos, hashPos > 0 ? hashPos : location2.length);
query = parseQuery$1(searchString.slice(1));
}
if (hashPos >= 0) {
path = path || location2.slice(0, hashPos);
hash = location2.slice(hashPos, location2.length);
}
path = resolveRelativePath(path != null ? path : location2, currentLocation);
return {
fullPath: path + searchString + hash,
path,
query,
hash: decode(hash)
};
}
function stringifyURL(stringifyQuery$1, location2) {
const query = location2.query ? stringifyQuery$1(location2.query) : "";
return location2.path + (query && "?") + query + (location2.hash || "");
}
function stripBase(pathname, base) {
if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase())) return pathname;
return pathname.slice(base.length) || "/";
}
function isSameRouteLocation(stringifyQuery$1, a, b) {
const aLastIndex = a.matched.length - 1;
const bLastIndex = b.matched.length - 1;
return aLastIndex > -1 && aLastIndex === bLastIndex && isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) && isSameRouteLocationParams(a.params, b.params) && stringifyQuery$1(a.query) === stringifyQuery$1(b.query) && a.hash === b.hash;
}
function isSameRouteRecord(a, b) {
return (a.aliasOf || a) === (b.aliasOf || b);
}
function isSameRouteLocationParams(a, b) {
if (Object.keys(a).length !== Object.keys(b).length) return false;
for (const key in a) if (!isSameRouteLocationParamsValue(a[key], b[key])) return false;
return true;
}
function isSameRouteLocationParamsValue(a, b) {
return isArray(a) ? isEquivalentArray(a, b) : isArray(b) ? isEquivalentArray(b, a) : a === b;
}
function isEquivalentArray(a, b) {
return isArray(b) ? a.length === b.length && a.every((value, i) => value === b[i]) : a.length === 1 && a[0] === b;
}
function resolveRelativePath(to, from) {
if (to.startsWith("/")) return to;
if (!from.startsWith("/")) {
warn$1(`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`);
return to;
}
if (!to) return from;
const fromSegments = from.split("/");
const toSegments = to.split("/");
const lastToSegment = toSegments[toSegments.length - 1];
if (lastToSegment === ".." || lastToSegment === ".") toSegments.push("");
let position = fromSegments.length - 1;
let toPosition;
let segment;
for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
segment = toSegments[toPosition];
if (segment === ".") continue;
if (segment === "..") {
if (position > 1) position--;
} else break;
}
return fromSegments.slice(0, position).join("/") + "/" + toSegments.slice(toPosition).join("/");
}
var START_LOCATION_NORMALIZED = {
path: "/",
name: void 0,
params: {},
query: {},
hash: "",
fullPath: "/",
matched: [],
meta: {},
redirectedFrom: void 0
};
var NavigationType = (function(NavigationType$1) {
NavigationType$1["pop"] = "pop";
NavigationType$1["push"] = "push";
return NavigationType$1;
})({});
var NavigationDirection = (function(NavigationDirection$1) {
NavigationDirection$1["back"] = "back";
NavigationDirection$1["forward"] = "forward";
NavigationDirection$1["unknown"] = "";
return NavigationDirection$1;
})({});
var START = "";
function normalizeBase(base) {
if (!base) if (isBrowser) {
const baseEl = document.querySelector("base");
base = baseEl && baseEl.getAttribute("href") || "/";
base = base.replace(/^\w+:\/\/[^\/]+/, "");
} else base = "/";
if (base[0] !== "/" && base[0] !== "#") base = "/" + base;
return removeTrailingSlash(base);
}
var BEFORE_HASH_RE = /^[^#]+#/;
function createHref(base, location2) {
return base.replace(BEFORE_HASH_RE, "#") + location2;
}
function getElementPosition(el, offset) {
const docRect = document.documentElement.getBoundingClientRect();
const elRect = el.getBoundingClientRect();
return {
behavior: offset.behavior,
left: elRect.left - docRect.left - (offset.left || 0),
top: elRect.top - docRect.top - (offset.top || 0)
};
}
var computeScrollPosition = () => ({
left: window.scrollX,
top: window.scrollY
});
function scrollToPosition(position) {
let scrollToOptions;
if ("el" in position) {
const positionEl = position.el;
const isIdSelector = typeof positionEl === "string" && positionEl.startsWith("#");
if (typeof position.el === "string") {
if (!isIdSelector || !document.getElementById(position.el.slice(1))) try {
const foundEl = document.querySelector(position.el);
if (isIdSelector && foundEl) {
warn$1(`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`);
return;
}
} catch (err) {
warn$1(`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`);
return;
}
}
const el = typeof positionEl === "string" ? isIdSelector ? document.getElementById(positionEl.slice(1)) : document.querySelector(positionEl) : positionEl;
if (!el) {
warn$1(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`);
return;
}
scrollToOptions = getElementPosition(el, position);
} else scrollToOptions = position;
if ("scrollBehavior" in document.documentElement.style) window.scrollTo(scrollToOptions);
else window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.scrollX, scrollToOptions.top != null ? scrollToOptions.top : window.scrollY);
}
function getScrollKey(path, delta) {
return (history.state ? history.state.position - delta : -1) + path;
}
var scrollPositions = /* @__PURE__ */ new Map();
function saveScrollPosition(key, scrollPosition) {
scrollPositions.set(key, scrollPosition);
}
function getSavedScrollPosition(key) {
const scroll = scrollPositions.get(key);
scrollPositions.delete(key);
return scroll;
}
function isRouteLocation(route) {
return typeof route === "string" || route && typeof route === "object";
}
function isRouteName(name) {
return typeof name === "string" || typeof name === "symbol";
}
var ErrorTypes = (function(ErrorTypes$1) {
ErrorTypes$1[ErrorTypes$1["MATCHER_NOT_FOUND"] = 1] = "MATCHER_NOT_FOUND";
ErrorTypes$1[ErrorTypes$1["NAVIGATION_GUARD_REDIRECT"] = 2] = "NAVIGATION_GUARD_REDIRECT";
ErrorTypes$1[ErrorTypes$1["NAVIGATION_ABORTED"] = 4] = "NAVIGATION_ABORTED";
ErrorTypes$1[ErrorTypes$1["NAVIGATION_CANCELLED"] = 8] = "NAVIGATION_CANCELLED";
ErrorTypes$1[ErrorTypes$1["NAVIGATION_DUPLICATED"] = 16] = "NAVIGATION_DUPLICATED";
return ErrorTypes$1;
})({});
var NavigationFailureSymbol = Symbol(true ? "navigation failure" : "");
var NavigationFailureType = (function(NavigationFailureType$1) {
NavigationFailureType$1[NavigationFailureType$1["aborted"] = 4] = "aborted";
NavigationFailureType$1[NavigationFailureType$1["cancelled"] = 8] = "cancelled";
NavigationFailureType$1[NavigationFailureType$1["duplicated"] = 16] = "duplicated";
return NavigationFailureType$1;
})({});
var ErrorTypeMessages = {
[ErrorTypes.MATCHER_NOT_FOUND]({ location: location2, currentLocation }) {
return `No match for
${JSON.stringify(location2)}${currentLocation ? "\nwhile being at\n" + JSON.stringify(currentLocation) : ""}`;
},
[ErrorTypes.NAVIGATION_GUARD_REDIRECT]({ from, to }) {
return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
},
[ErrorTypes.NAVIGATION_ABORTED]({ from, to }) {
return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
},
[ErrorTypes.NAVIGATION_CANCELLED]({ from, to }) {
return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
},
[ErrorTypes.NAVIGATION_DUPLICATED]({ from, to }) {
return `Avoided redundant navigation to current location: "${from.fullPath}".`;
}
};
function createRouterError(type, params) {
if (true) return assign(new Error(ErrorTypeMessages[type](params)), {
type,
[NavigationFailureSymbol]: true
}, params);
else return assign(new Error(), {
type,
[NavigationFailureSymbol]: true
}, params);
}
function isNavigationFailure(error, type) {
return error instanceof Error && NavigationFailureSymbol in error && (type == null || !!(error.type & type));
}
var propertiesToLog = [
"params",
"query",
"hash"
];
function stringifyRoute(to) {
if (typeof to === "string") return to;
if (to.path != null) return to.path;
const location2 = {};
for (const key of propertiesToLog) if (key in to) location2[key] = to[key];
return JSON.stringify(location2, null, 2);
}
function parseQuery(search) {
const query = {};
if (search === "" || search === "?") return query;
const searchParams = (search[0] === "?" ? search.slice(1) : search).split("&");
for (let i = 0; i < searchParams.length; ++i) {
const searchParam = searchParams[i].replace(PLUS_RE, " ");
const eqPos = searchParam.indexOf("=");
const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
if (key in query) {
let currentValue = query[key];
if (!isArray(currentValue)) currentValue = query[key] = [currentValue];
currentValue.push(value);
} else query[key] = value;
}
return query;
}
function stringifyQuery(query) {
let search = "";
for (let key in query) {
const value = query[key];
key = encodeQueryKey(key);
if (value == null) {
if (value !== void 0) search += (search.length ? "&" : "") + key;
continue;
}
(isArray(value) ? value.map((v) => v && encodeQueryValue(v)) : [value && encodeQueryValue(value)]).forEach((value$1) => {
if (value$1 !== void 0) {
search += (search.length ? "&" : "") + key;
if (value$1 != null) search += "=" + value$1;
}
});
}
return search;
}
function normalizeQuery(query) {
const normalizedQuery = {};
for (const key in query) {
const value = query[key];
if (value !== void 0) normalizedQuery[key] = isArray(value) ? value.map((v) => v == null ? null : "" + v) : value == null ? value : "" + value;
}
return normalizedQuery;
}
var matchedRouteKey = Symbol(true ? "router view location matched" : "");
var viewDepthKey = Symbol(true ? "router view depth" : "");
var routerKey = Symbol(true ? "router" : "");
var routeLocationKey = Symbol(true ? "route location" : "");
var routerViewLocationKey = Symbol(true ? "router view location" : "");
function useCallbacks() {
let handlers = [];
function add(handler) {
handlers.push(handler);
return () => {
const i = handlers.indexOf(handler);
if (i > -1) handlers.splice(i, 1);
};
}
function reset() {
handlers = [];
}
return {
add,
list: () => handlers.slice(),
reset
};
}
function registerGuard(record, name, guard) {
const removeFromList = () => {
record[name].delete(guard);
};
onUnmounted(removeFromList);
onDeactivated(removeFromList);
onActivated(() => {
record[name].add(guard);
});
record[name].add(guard);
}
function onBeforeRouteLeave(leaveGuard) {
if (!getCurrentInstance()) {
warn$1("getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function");
return;
}
const activeRecord = inject(matchedRouteKey, {}).value;
if (!activeRecord) {
warn$1("No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside a component child of <router-view>. Maybe you called it inside of App.vue?");
return;
}
registerGuard(activeRecord, "leaveGuards", leaveGuard);
}
function onBeforeRouteUpdate(updateGuard) {
if (!getCurrentInstance()) {
warn$1("getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function");
return;
}
const activeRecord = inject(matchedRouteKey, {}).value;
if (!activeRecord) {
warn$1("No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside a component child of <router-view>. Maybe you called it inside of App.vue?");
return;
}
registerGuard(activeRecord, "updateGuards", updateGuard);
}
function guardToPromiseFn(guard, to, from, record, name, runWithContext = (fn) => fn()) {
const enterCallbackArray = record && (record.enterCallbacks[name] = record.enterCallbacks[name] || []);
return () => new Promise((resolve, reject) => {
const next = (valid) => {
if (valid === false) reject(createRouterError(ErrorTypes.NAVIGATION_ABORTED, {
from,
to
}));
else if (valid instanceof Error) reject(valid);
else if (isRouteLocation(valid)) reject(createRouterError(ErrorTypes.NAVIGATION_GUARD_REDIRECT, {
from: to,
to: valid
}));
else {
if (enterCallbackArray && record.enterCallbacks[name] === enterCallbackArray && typeof valid === "function") enterCallbackArray.push(valid);
resolve();
}
};
const guardReturn = runWithContext(() => guard.call(record && record.instances[name], to, from, true ? canOnlyBeCalledOnce(next, to, from) : next));
let guardCall = Promise.resolve(guardReturn);
if (guard.length < 3) guardCall = guardCall.then(next);
if (guard.length > 2) {
const message = `The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ""}:
${guard.toString()}
. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
if (typeof guardReturn === "object" && "then" in guardReturn) guardCall = guardCall.then((resolvedValue) => {
if (!next._called) {
warn$1(message);
return Promise.reject(new Error("Invalid navigation guard"));
}
return resolvedValue;
});
else if (guardReturn !== void 0) {
if (!next._called) {
warn$1(message);
reject(new Error("Invalid navigation guard"));
return;
}
}
}
guardCall.catch((err) => reject(err));
});
}
function canOnlyBeCalledOnce(next, to, from) {
let called = 0;
return function() {
if (called++ === 1) warn$1(`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`);
next._called = true;
if (called === 1) next.apply(null, arguments);
};
}
function extractComponentsGuards(matched, guardType, to, from, runWithContext = (fn) => fn()) {
const guards = [];
for (const record of matched) {
if (!record.components && record.children && !record.children.length) warn$1(`Record with path "${record.path}" is either missing a "component(s)" or "children" property.`);
for (const name in record.components) {
let rawComponent = record.components[name];
if (true) {
if (!rawComponent || typeof rawComponent !== "object" && typeof rawComponent !== "function") {
warn$1(`Component "${name}" in record with path "${record.path}" is not a valid component. Received "${String(rawComponent)}".`);
throw new Error("Invalid route component");
} else if ("then" in rawComponent) {
warn$1(`Component "${name}" in record with path "${record.path}" is a Promise instead of a function that returns a Promise. Did you write "import('./MyPage.vue')" instead of "() => import('./MyPage.vue')" ? This will break in production if not fixed.`);
const promise = rawComponent;
rawComponent = () => promise;
} else if (rawComponent.__asyncLoader && !rawComponent.__warnedDefineAsync) {
rawComponent.__warnedDefineAsync = true;
warn$1(`Component "${name}" in record with path "${record.path}" is defined using "defineAsyncComponent()". Write "() => import('./MyPage.vue')" instead of "defineAsyncComponent(() => import('./MyPage.vue'))".`);
}
}
if (guardType !== "beforeRouteEnter" && !record.instances[name]) continue;
if (isRouteComponent(rawComponent)) {
const guard = (rawComponent.__vccOpts || rawComponent)[guardType];
guard && guards.push(guardToPromiseFn(guard, to, from, record, name, runWithContext));
} else {
let componentPromise = rawComponent();
if (!("catch" in componentPromise)) {
warn$1(`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`);
componentPromise = Promise.resolve(componentPromise);
}
guards.push(() => componentPromise.then((resolved) => {
if (!resolved) throw new Error(`Couldn't resolve component "${name}" at "${record.path}"`);
const resolvedComponent = isESModule(resolved) ? resolved.default : resolved;
record.mods[name] = resolved;
record.components[name] = resolvedComponent;
const guard = (resolvedComponent.__vccOpts || resolvedComponent)[guardType];
return guard && guardToPromiseFn(guard, to, from, record, name, runWithContext)();
}));
}
}
}
return guards;
}
function loadRouteLocation(route) {
return route.matched.every((record) => record.redirect) ? Promise.reject(new Error("Cannot load a route that redirects.")) : Promise.all(route.matched.map((record) => record.components && Promise.all(Object.keys(record.components).reduce((promises, name) => {
const rawComponent = record.components[name];
if (typeof rawComponent === "function" && !("displayName" in rawComponent)) promises.push(rawComponent().then((resolved) => {
if (!resolved) return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.`));
const resolvedComponent = isESModule(resolved) ? resolved.default : resolved;
record.mods[name] = resolved;
record.components[name] = resolvedComponent;
}));
return promises;
}, [])))).then(() => route);
}
function extractChangingRecords(to, from) {
const leavingRecords = [];
const updatingRecords = [];
const enteringRecords = [];
const len = Math.max(from.matched.length, to.matched.length);
for (let i = 0; i < len; i++) {
const recordFrom = from.matched[i];
if (recordFrom) if (to.matched.find((record) => isSameRouteRecord(record, recordFrom))) updatingRecords.push(recordFrom);
else leavingRecords.push(recordFrom);
const recordTo = to.matched[i];
if (recordTo) {
if (!from.matched.find((record) => isSameRouteRecord(record, recordTo))) enteringRecords.push(recordTo);
}
}
return [
leavingRecords,
updatingRecords,
enteringRecords
];
}
function formatRouteLocation(routeLocation, tooltip) {
const copy = assign({}, routeLocation, { matched: routeLocation.matched.map((matched) => omit(matched, [
"instances",
"children",
"aliasOf"
])) });
return { _custom: {
type: null,
readOnly: true,
display: routeLocation.fullPath,
tooltip,
value: copy
} };
}
function formatDisplay(display) {
return { _custom: { display } };
}
var routerId = 0;
function addDevtools(app, router, matcher) {
if (router.__hasDevtools) return;
router.__hasDevtools = true;
const id = routerId++;
setupDevtoolsPlugin({
id: "org.vuejs.router" + (id ? "." + id : ""),
label: "Vue Router",
packageName: "vue-router",
homepage: "https://router.vuejs.org",
logo: "https://router.vuejs.org/logo.png",
componentStateTypes: ["Routing"],
app
}, (api) => {
if (typeof api.now !== "function") warn$1("[Vue Router]: You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.");
api.on.inspectComponent((payload, ctx) => {
if (payload.instanceData) payload.instanceData.state.push({
type: "Routing",
key: "$route",
editable: false,
value: formatRouteLocation(router.currentRoute.value, "Current Route")
});
});
api.on.visitComponentTree(({ treeNode: node, componentInstance }) => {
if (componentInstance.__vrv_devtools) {
const info = componentInstance.__vrv_devtools;
node.tags.push({
label: (info.name ? `${info.name.toString()}: ` : "") + info.path,
textColor: 0,
tooltip: "This component is rendered by &lt;router-view&gt;",
backgroundColor: PINK_500
});
}
if (isArray(componentInstance.__vrl_devtools)) {
componentInstance.__devtoolsApi = api;
componentInstance.__vrl_devtools.forEach((devtoolsData) => {
let label = devtoolsData.route.path;
let backgroundColor = ORANGE_400;
let tooltip = "";
let textColor = 0;
if (devtoolsData.error) {
label = devtoolsData.error;
backgroundColor = RED_100;
textColor = RED_700;
} else if (devtoolsData.isExactActive) {
backgroundColor = LIME_500;
tooltip = "This is exactly active";
} else if (devtoolsData.isActive) {
backgroundColor = BLUE_600;
tooltip = "This link is active";
}
node.tags.push({
label,
textColor,
tooltip,
backgroundColor
});
});
}
});
watch(router.currentRoute, () => {
refreshRoutesView();
api.notifyComponentUpdate();
api.sendInspectorTree(routerInspectorId);
api.sendInspectorState(routerInspectorId);
});
const navigationsLayerId = "router:navigations:" + id;
api.addTimelineLayer({
id: navigationsLayerId,
label: `Router${id ? " " + id : ""} Navigations`,
color: 4237508
});
router.onError((error, to) => {
api.addTimelineEvent({
layerId: navigationsLayerId,
event: {
title: "Error during Navigation",
subtitle: to.fullPath,
logType: "error",
time: api.now(),
data: { error },
groupId: to.meta.__navigationId
}
});
});
let navigationId = 0;
router.beforeEach((to, from) => {
const data = {
guard: formatDisplay("beforeEach"),
from: formatRouteLocation(from, "Current Location during this navigation"),
to: formatRouteLocation(to, "Target location")
};
Object.defineProperty(to.meta, "__navigationId", { value: navigationId++ });
api.addTimelineEvent({
layerId: navigationsLayerId,
event: {
time: api.now(),
title: "Start of navigation",
subtitle: to.fullPath,
data,
groupId: to.meta.__navigationId
}
});
});
router.afterEach((to, from, failure) => {
const data = { guard: formatDisplay("afterEach") };
if (failure) {
data.failure = { _custom: {
type: Error,
readOnly: true,
display: failure ? failure.message : "",
tooltip: "Navigation Failure",
value: failure
} };
data.status = formatDisplay("❌");
} else data.status = formatDisplay("✅");
data.from = formatRouteLocation(from, "Current Location during this navigation");
data.to = formatRouteLocation(to, "Target location");
api.addTimelineEvent({
layerId: navigationsLayerId,
event: {
title: "End of navigation",
subtitle: to.fullPath,
time: api.now(),
data,
logType: failure ? "warning" : "default",
groupId: to.meta.__navigationId
}
});
});
const routerInspectorId = "router-inspector:" + id;
api.addInspector({
id: routerInspectorId,
label: "Routes" + (id ? " " + id : ""),
icon: "book",
treeFilterPlaceholder: "Search routes"
});
function refreshRoutesView() {
if (!activeRoutesPayload) return;
const payload = activeRoutesPayload;
let routes = matcher.getRoutes().filter((route) => !route.parent || !route.parent.record.components);
routes.forEach(resetMatchStateOnRouteRecord);
if (payload.filter) routes = routes.filter((route) => isRouteMatching(route, payload.filter.toLowerCase()));
routes.forEach((route) => markRouteRecordActive(route, router.currentRoute.value));
payload.rootNodes = routes.map(formatRouteRecordForInspector);
}
let activeRoutesPayload;
api.on.getInspectorTree((payload) => {
activeRoutesPayload = payload;
if (payload.app === app && payload.inspectorId === routerInspectorId) refreshRoutesView();
});
api.on.getInspectorState((payload) => {
if (payload.app === app && payload.inspectorId === routerInspectorId) {
const route = matcher.getRoutes().find((route$1) => route$1.record.__vd_id === payload.nodeId);
if (route) payload.state = { options: formatRouteRecordMatcherForStateInspector(route) };
}
});
api.sendInspectorTree(routerInspectorId);
api.sendInspectorState(routerInspectorId);
});
}
function modifierForKey(key) {
if (key.optional) return key.repeatable ? "*" : "?";
else return key.repeatable ? "+" : "";
}
function formatRouteRecordMatcherForStateInspector(route) {
const { record } = route;
const fields = [{
editable: false,
key: "path",
value: record.path
}];
if (record.name != null) fields.push({
editable: false,
key: "name",
value: record.name
});
fields.push({
editable: false,
key: "regexp",
value: route.re
});
if (route.keys.length) fields.push({
editable: false,
key: "keys",
value: { _custom: {
type: null,
readOnly: true,
display: route.keys.map((key) => `${key.name}${modifierForKey(key)}`).join(" "),
tooltip: "Param keys",
value: route.keys
} }
});
if (record.redirect != null) fields.push({
editable: false,
key: "redirect",
value: record.redirect
});
if (route.alias.length) fields.push({
editable: false,
key: "aliases",
value: route.alias.map((alias) => alias.record.path)
});
if (Object.keys(route.record.meta).length) fields.push({
editable: false,
key: "meta",
value: route.record.meta
});
fields.push({
key: "score",
editable: false,
value: { _custom: {
type: null,
readOnly: true,
display: route.score.map((score) => score.join(", ")).join(" | "),
tooltip: "Score used to sort routes",
value: route.score
} }
});
return fields;
}
var PINK_500 = 15485081;
var BLUE_600 = 2450411;
var LIME_500 = 8702998;
var CYAN_400 = 2282478;
var ORANGE_400 = 16486972;
var DARK = 6710886;
var RED_100 = 16704226;
var RED_700 = 12131356;
function formatRouteRecordForInspector(route) {
const tags = [];
const { record } = route;
if (record.name != null) tags.push({
label: String(record.name),
textColor: 0,
backgroundColor: CYAN_400
});
if (record.aliasOf) tags.push({
label: "alias",
textColor: 0,
backgroundColor: ORANGE_400
});
if (route.__vd_match) tags.push({
label: "matches",
textColor: 0,
backgroundColor: PINK_500
});
if (route.__vd_exactActive) tags.push({
label: "exact",
textColor: 0,
backgroundColor: LIME_500
});
if (route.__vd_active) tags.push({
label: "active",
textColor: 0,
backgroundColor: BLUE_600
});
if (record.redirect) tags.push({
label: typeof record.redirect === "string" ? `redirect: ${record.redirect}` : "redirects",
textColor: 16777215,
backgroundColor: DARK
});
let id = record.__vd_id;
if (id == null) {
id = String(routeRecordId++);
record.__vd_id = id;
}
return {
id,
label: record.path,
tags,
children: route.children.map(formatRouteRecordForInspector)
};
}
var routeRecordId = 0;
var EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/;
function markRouteRecordActive(route, currentRoute) {
const isExactActive = currentRoute.matched.length && isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
route.__vd_exactActive = route.__vd_active = isExactActive;
if (!isExactActive) route.__vd_active = currentRoute.matched.some((match) => isSameRouteRecord(match, route.record));
route.children.forEach((childRoute) => markRouteRecordActive(childRoute, currentRoute));
}
function resetMatchStateOnRouteRecord(route) {
route.__vd_match = false;
route.children.forEach(resetMatchStateOnRouteRecord);
}
function isRouteMatching(route, filter) {
const found = String(route.re).match(EXTRACT_REGEXP_RE);
route.__vd_match = false;
if (!found || found.length < 3) return false;
if (new RegExp(found[1].replace(/\$$/, ""), found[2]).test(filter)) {
route.children.forEach((child) => isRouteMatching(child, filter));
if (route.record.path !== "/" || filter === "/") {
route.__vd_match = route.re.test(filter);
return true;
}
return false;
}
const path = route.record.path.toLowerCase();
const decodedPath = decode(path);
if (!filter.startsWith("/") && (decodedPath.includes(filter) || path.includes(filter))) return true;
if (decodedPath.startsWith(filter) || path.startsWith(filter)) return true;
if (route.record.name && String(route.record.name).includes(filter)) return true;
return route.children.some((child) => isRouteMatching(child, filter));
}
function omit(obj, keys) {
const ret = {};
for (const key in obj) if (!keys.includes(key)) ret[key] = obj[key];
return ret;
}
// node_modules/vue-router/dist/vue-router.mjs
var createBaseLocation = () => location.protocol + "//" + location.host;
function createCurrentLocation(base, location$1) {
const { pathname, search, hash } = location$1;
const hashPos = base.indexOf("#");
if (hashPos > -1) {
let slicePos = hash.includes(base.slice(hashPos)) ? base.slice(hashPos).length : 1;
let pathFromHash = hash.slice(slicePos);
if (pathFromHash[0] !== "/") pathFromHash = "/" + pathFromHash;
return stripBase(pathFromHash, "");
}
return stripBase(pathname, base) + search + hash;
}
function useHistoryListeners(base, historyState, currentLocation, replace) {
let listeners = [];
let teardowns = [];
let pauseState = null;
const popStateHandler = ({ state }) => {
const to = createCurrentLocation(base, location);
const from = currentLocation.value;
const fromState = historyState.value;
let delta = 0;
if (state) {
currentLocation.value = to;
historyState.value = state;
if (pauseState && pauseState === from) {
pauseState = null;
return;
}
delta = fromState ? state.position - fromState.position : 0;
} else replace(to);
listeners.forEach((listener) => {
listener(currentLocation.value, from, {
delta,
type: NavigationType.pop,
direction: delta ? delta > 0 ? NavigationDirection.forward : NavigationDirection.back : NavigationDirection.unknown
});
});
};
function pauseListeners() {
pauseState = currentLocation.value;
}
function listen(callback) {
listeners.push(callback);
const teardown = () => {
const index = listeners.indexOf(callback);
if (index > -1) listeners.splice(index, 1);
};
teardowns.push(teardown);
return teardown;
}
function beforeUnloadListener() {
if (document.visibilityState === "hidden") {
const { history: history$1 } = window;
if (!history$1.state) return;
history$1.replaceState(assign({}, history$1.state, { scroll: computeScrollPosition() }), "");
}
}
function destroy() {
for (const teardown of teardowns) teardown();
teardowns = [];
window.removeEventListener("popstate", popStateHandler);
window.removeEventListener("pagehide", beforeUnloadListener);
document.removeEventListener("visibilitychange", beforeUnloadListener);
}
window.addEventListener("popstate", popStateHandler);
window.addEventListener("pagehide", beforeUnloadListener);
document.addEventListener("visibilitychange", beforeUnloadListener);
return {
pauseListeners,
listen,
destroy
};
}
function buildState(back, current, forward, replaced = false, computeScroll = false) {
return {
back,
current,
forward,
replaced,
position: window.history.length,
scroll: computeScroll ? computeScrollPosition() : null
};
}
function useHistoryStateNavigation(base) {
const { history: history$1, location: location$1 } = window;
const currentLocation = { value: createCurrentLocation(base, location$1) };
const historyState = { value: history$1.state };
if (!historyState.value) changeLocation(currentLocation.value, {
back: null,
current: currentLocation.value,
forward: null,
position: history$1.length - 1,
replaced: true,
scroll: null
}, true);
function changeLocation(to, state, replace$1) {
const hashIndex = base.indexOf("#");
const url = hashIndex > -1 ? (location$1.host && document.querySelector("base") ? base : base.slice(hashIndex)) + to : createBaseLocation() + base + to;
try {
history$1[replace$1 ? "replaceState" : "pushState"](state, "", url);
historyState.value = state;
} catch (err) {
if (true) warn$1("Error with push/replace State", err);
else console.error(err);
location$1[replace$1 ? "replace" : "assign"](url);
}
}
function replace(to, data) {
changeLocation(to, assign({}, history$1.state, buildState(historyState.value.back, to, historyState.value.forward, true), data, { position: historyState.value.position }), true);
currentLocation.value = to;
}
function push(to, data) {
const currentState = assign({}, historyState.value, history$1.state, {
forward: to,
scroll: computeScrollPosition()
});
if (!history$1.state) warn$1("history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\nhistory.replaceState(history.state, '', url)\n\nYou can find more information at https://router.vuejs.org/guide/migration/#Usage-of-history-state");
changeLocation(currentState.current, currentState, true);
changeLocation(to, assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data), false);
currentLocation.value = to;
}
return {
location: currentLocation,
state: historyState,
push,
replace
};
}
function createWebHistory(base) {
base = normalizeBase(base);
const historyNavigation = useHistoryStateNavigation(base);
const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace);
function go(delta, triggerListeners = true) {
if (!triggerListeners) historyListeners.pauseListeners();
history.go(delta);
}
const routerHistory = assign({
location: "",
base,
go,
createHref: createHref.bind(null, base)
}, historyNavigation, historyListeners);
Object.defineProperty(routerHistory, "location", {
enumerable: true,
get: () => historyNavigation.location.value
});
Object.defineProperty(routerHistory, "state", {
enumerable: true,
get: () => historyNavigation.state.value
});
return routerHistory;
}
function createMemoryHistory(base = "") {
let listeners = [];
let queue = [[START, {}]];
let position = 0;
base = normalizeBase(base);
function setLocation(location$1, state = {}) {
position++;
if (position !== queue.length) queue.splice(position);
queue.push([location$1, state]);
}
function triggerListeners(to, from, { direction, delta }) {
const info = {
direction,
delta,
type: NavigationType.pop
};
for (const callback of listeners) callback(to, from, info);
}
const routerHistory = {
location: START,
state: {},
base,
createHref: createHref.bind(null, base),
replace(to, state) {
queue.splice(position--, 1);
setLocation(to, state);
},
push(to, state) {
setLocation(to, state);
},
listen(callback) {
listeners.push(callback);
return () => {
const index = listeners.indexOf(callback);
if (index > -1) listeners.splice(index, 1);
};
},
destroy() {
listeners = [];
queue = [[START, {}]];
position = 0;
},
go(delta, shouldTrigger = true) {
const from = this.location;
const direction = delta < 0 ? NavigationDirection.back : NavigationDirection.forward;
position = Math.max(0, Math.min(position + delta, queue.length - 1));
if (shouldTrigger) triggerListeners(this.location, from, {
direction,
delta
});
}
};
Object.defineProperty(routerHistory, "location", {
enumerable: true,
get: () => queue[position][0]
});
Object.defineProperty(routerHistory, "state", {
enumerable: true,
get: () => queue[position][1]
});
return routerHistory;
}
function createWebHashHistory(base) {
base = location.host ? base || location.pathname + location.search : "";
if (!base.includes("#")) base += "#";
if (!base.endsWith("#/") && !base.endsWith("#")) warn$1(`A hash base must end with a "#":
"${base}" should be "${base.replace(/#.*$/, "#")}".`);
return createWebHistory(base);
}
var TokenType = (function(TokenType$1) {
TokenType$1[TokenType$1["Static"] = 0] = "Static";
TokenType$1[TokenType$1["Param"] = 1] = "Param";
TokenType$1[TokenType$1["Group"] = 2] = "Group";
return TokenType$1;
})({});
var TokenizerState = (function(TokenizerState$1) {
TokenizerState$1[TokenizerState$1["Static"] = 0] = "Static";
TokenizerState$1[TokenizerState$1["Param"] = 1] = "Param";
TokenizerState$1[TokenizerState$1["ParamRegExp"] = 2] = "ParamRegExp";
TokenizerState$1[TokenizerState$1["ParamRegExpEnd"] = 3] = "ParamRegExpEnd";
TokenizerState$1[TokenizerState$1["EscapeNext"] = 4] = "EscapeNext";
return TokenizerState$1;
})(TokenizerState || {});
var ROOT_TOKEN = {
type: TokenType.Static,
value: ""
};
var VALID_PARAM_RE = /[a-zA-Z0-9_]/;
function tokenizePath(path) {
if (!path) return [[]];
if (path === "/") return [[ROOT_TOKEN]];
if (!path.startsWith("/")) throw new Error(true ? `Route paths should start with a "/": "${path}" should be "/${path}".` : `Invalid path "${path}"`);
function crash(message) {
throw new Error(`ERR (${state})/"${buffer}": ${message}`);
}
let state = TokenizerState.Static;
let previousState = state;
const tokens = [];
let segment;
function finalizeSegment() {
if (segment) tokens.push(segment);
segment = [];
}
let i = 0;
let char;
let buffer = "";
let customRe = "";
function consumeBuffer() {
if (!buffer) return;
if (state === TokenizerState.Static) segment.push({
type: TokenType.Static,
value: buffer
});
else if (state === TokenizerState.Param || state === TokenizerState.ParamRegExp || state === TokenizerState.ParamRegExpEnd) {
if (segment.length > 1 && (char === "*" || char === "+")) crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
segment.push({
type: TokenType.Param,
value: buffer,
regexp: customRe,
repeatable: char === "*" || char === "+",
optional: char === "*" || char === "?"
});
} else crash("Invalid state to consume buffer");
buffer = "";
}
function addCharToBuffer() {
buffer += char;
}
while (i < path.length) {
char = path[i++];
if (char === "\\" && state !== TokenizerState.ParamRegExp) {
previousState = state;
state = TokenizerState.EscapeNext;
continue;
}
switch (state) {
case TokenizerState.Static:
if (char === "/") {
if (buffer) consumeBuffer();
finalizeSegment();
} else if (char === ":") {
consumeBuffer();
state = TokenizerState.Param;
} else addCharToBuffer();
break;
case TokenizerState.EscapeNext:
addCharToBuffer();
state = previousState;
break;
case TokenizerState.Param:
if (char === "(") state = TokenizerState.ParamRegExp;
else if (VALID_PARAM_RE.test(char)) addCharToBuffer();
else {
consumeBuffer();
state = TokenizerState.Static;
if (char !== "*" && char !== "?" && char !== "+") i--;
}
break;
case TokenizerState.ParamRegExp:
if (char === ")") if (customRe[customRe.length - 1] == "\\") customRe = customRe.slice(0, -1) + char;
else state = TokenizerState.ParamRegExpEnd;
else customRe += char;
break;
case TokenizerState.ParamRegExpEnd:
consumeBuffer();
state = TokenizerState.Static;
if (char !== "*" && char !== "?" && char !== "+") i--;
customRe = "";
break;
default:
crash("Unknown state");
break;
}
}
if (state === TokenizerState.ParamRegExp) crash(`Unfinished custom RegExp for param "${buffer}"`);
consumeBuffer();
finalizeSegment();
return tokens;
}
var BASE_PARAM_PATTERN = "[^/]+?";
var BASE_PATH_PARSER_OPTIONS = {
sensitive: false,
strict: false,
start: true,
end: true
};
var PathScore = (function(PathScore$1) {
PathScore$1[PathScore$1["_multiplier"] = 10] = "_multiplier";
PathScore$1[PathScore$1["Root"] = 90] = "Root";
PathScore$1[PathScore$1["Segment"] = 40] = "Segment";
PathScore$1[PathScore$1["SubSegment"] = 30] = "SubSegment";
PathScore$1[PathScore$1["Static"] = 40] = "Static";
PathScore$1[PathScore$1["Dynamic"] = 20] = "Dynamic";
PathScore$1[PathScore$1["BonusCustomRegExp"] = 10] = "BonusCustomRegExp";
PathScore$1[PathScore$1["BonusWildcard"] = -50] = "BonusWildcard";
PathScore$1[PathScore$1["BonusRepeatable"] = -20] = "BonusRepeatable";
PathScore$1[PathScore$1["BonusOptional"] = -8] = "BonusOptional";
PathScore$1[PathScore$1["BonusStrict"] = 0.7000000000000001] = "BonusStrict";
PathScore$1[PathScore$1["BonusCaseSensitive"] = 0.25] = "BonusCaseSensitive";
return PathScore$1;
})(PathScore || {});
var REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g;
function tokensToParser(segments, extraOptions) {
const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions);
const score = [];
let pattern = options.start ? "^" : "";
const keys = [];
for (const segment of segments) {
const segmentScores = segment.length ? [] : [PathScore.Root];
if (options.strict && !segment.length) pattern += "/";
for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
const token = segment[tokenIndex];
let subSegmentScore = PathScore.Segment + (options.sensitive ? PathScore.BonusCaseSensitive : 0);
if (token.type === TokenType.Static) {
if (!tokenIndex) pattern += "/";
pattern += token.value.replace(REGEX_CHARS_RE, "\\$&");
subSegmentScore += PathScore.Static;
} else if (token.type === TokenType.Param) {
const { value, repeatable, optional, regexp } = token;
keys.push({
name: value,
repeatable,
optional
});
const re$1 = regexp ? regexp : BASE_PARAM_PATTERN;
if (re$1 !== BASE_PARAM_PATTERN) {
subSegmentScore += PathScore.BonusCustomRegExp;
try {
`${re$1}`;
} catch (err) {
throw new Error(`Invalid custom RegExp for param "${value}" (${re$1}): ` + err.message);
}
}
let subPattern = repeatable ? `((?:${re$1})(?:/(?:${re$1}))*)` : `(${re$1})`;
if (!tokenIndex) subPattern = optional && segment.length < 2 ? `(?:/${subPattern})` : "/" + subPattern;
if (optional) subPattern += "?";
pattern += subPattern;
subSegmentScore += PathScore.Dynamic;
if (optional) subSegmentScore += PathScore.BonusOptional;
if (repeatable) subSegmentScore += PathScore.BonusRepeatable;
if (re$1 === ".*") subSegmentScore += PathScore.BonusWildcard;
}
segmentScores.push(subSegmentScore);
}
score.push(segmentScores);
}
if (options.strict && options.end) {
const i = score.length - 1;
score[i][score[i].length - 1] += PathScore.BonusStrict;
}
if (!options.strict) pattern += "/?";
if (options.end) pattern += "$";
else if (options.strict && !pattern.endsWith("/")) pattern += "(?:/|$)";
const re = new RegExp(pattern, options.sensitive ? "" : "i");
function parse(path) {
const match = path.match(re);
const params = {};
if (!match) return null;
for (let i = 1; i < match.length; i++) {
const value = match[i] || "";
const key = keys[i - 1];
params[key.name] = value && key.repeatable ? value.split("/") : value;
}
return params;
}
function stringify(params) {
let path = "";
let avoidDuplicatedSlash = false;
for (const segment of segments) {
if (!avoidDuplicatedSlash || !path.endsWith("/")) path += "/";
avoidDuplicatedSlash = false;
for (const token of segment) if (token.type === TokenType.Static) path += token.value;
else if (token.type === TokenType.Param) {
const { value, repeatable, optional } = token;
const param = value in params ? params[value] : "";
if (isArray(param) && !repeatable) throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
const text = isArray(param) ? param.join("/") : param;
if (!text) if (optional) {
if (segment.length < 2) if (path.endsWith("/")) path = path.slice(0, -1);
else avoidDuplicatedSlash = true;
} else throw new Error(`Missing required param "${value}"`);
path += text;
}
}
return path || "/";
}
return {
re,
score,
keys,
parse,
stringify
};
}
function compareScoreArray(a, b) {
let i = 0;
while (i < a.length && i < b.length) {
const diff = b[i] - a[i];
if (diff) return diff;
i++;
}
if (a.length < b.length) return a.length === 1 && a[0] === PathScore.Static + PathScore.Segment ? -1 : 1;
else if (a.length > b.length) return b.length === 1 && b[0] === PathScore.Static + PathScore.Segment ? 1 : -1;
return 0;
}
function comparePathParserScore(a, b) {
let i = 0;
const aScore = a.score;
const bScore = b.score;
while (i < aScore.length && i < bScore.length) {
const comp = compareScoreArray(aScore[i], bScore[i]);
if (comp) return comp;
i++;
}
if (Math.abs(bScore.length - aScore.length) === 1) {
if (isLastScoreNegative(aScore)) return 1;
if (isLastScoreNegative(bScore)) return -1;
}
return bScore.length - aScore.length;
}
function isLastScoreNegative(score) {
const last = score[score.length - 1];
return score.length > 0 && last[last.length - 1] < 0;
}
var PATH_PARSER_OPTIONS_DEFAULTS = {
strict: false,
end: true,
sensitive: false
};
function createRouteRecordMatcher(record, parent, options) {
const parser = tokensToParser(tokenizePath(record.path), options);
if (true) {
const existingKeys = /* @__PURE__ */ new Set();
for (const key of parser.keys) {
if (existingKeys.has(key.name)) warn$1(`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`);
existingKeys.add(key.name);
}
}
const matcher = assign(parser, {
record,
parent,
children: [],
alias: []
});
if (parent) {
if (!matcher.record.aliasOf === !parent.record.aliasOf) parent.children.push(matcher);
}
return matcher;
}
function createRouterMatcher(routes, globalOptions) {
const matchers = [];
const matcherMap = /* @__PURE__ */ new Map();
globalOptions = mergeOptions(PATH_PARSER_OPTIONS_DEFAULTS, globalOptions);
function getRecordMatcher(name) {
return matcherMap.get(name);
}
function addRoute(record, parent, originalRecord) {
const isRootAdd = !originalRecord;
const mainNormalizedRecord = normalizeRouteRecord(record);
if (true) checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent);
mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
const options = mergeOptions(globalOptions, record);
const normalizedRecords = [mainNormalizedRecord];
if ("alias" in record) {
const aliases = typeof record.alias === "string" ? [record.alias] : record.alias;
for (const alias of aliases) normalizedRecords.push(normalizeRouteRecord(assign({}, mainNormalizedRecord, {
components: originalRecord ? originalRecord.record.components : mainNormalizedRecord.components,
path: alias,
aliasOf: originalRecord ? originalRecord.record : mainNormalizedRecord
})));
}
let matcher;
let originalMatcher;
for (const normalizedRecord of normalizedRecords) {
const { path } = normalizedRecord;
if (parent && path[0] !== "/") {
const parentPath = parent.record.path;
const connectingSlash = parentPath[parentPath.length - 1] === "/" ? "" : "/";
normalizedRecord.path = parent.record.path + (path && connectingSlash + path);
}
if (normalizedRecord.path === "*") throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\nSee more at https://router.vuejs.org/guide/migration/#Removed-star-or-catch-all-routes.');
matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
if (parent && path[0] === "/") checkMissingParamsInAbsolutePath(matcher, parent);
if (originalRecord) {
originalRecord.alias.push(matcher);
if (true) checkSameParams(originalRecord, matcher);
} else {
originalMatcher = originalMatcher || matcher;
if (originalMatcher !== matcher) originalMatcher.alias.push(matcher);
if (isRootAdd && record.name && !isAliasRecord(matcher)) {
if (true) checkSameNameAsAncestor(record, parent);
removeRoute(record.name);
}
}
if (isMatchable(matcher)) insertMatcher(matcher);
if (mainNormalizedRecord.children) {
const children = mainNormalizedRecord.children;
for (let i = 0; i < children.length; i++) addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
}
originalRecord = originalRecord || matcher;
}
return originalMatcher ? () => {
removeRoute(originalMatcher);
} : noop;
}
function removeRoute(matcherRef) {
if (isRouteName(matcherRef)) {
const matcher = matcherMap.get(matcherRef);
if (matcher) {
matcherMap.delete(matcherRef);
matchers.splice(matchers.indexOf(matcher), 1);
matcher.children.forEach(removeRoute);
matcher.alias.forEach(removeRoute);
}
} else {
const index = matchers.indexOf(matcherRef);
if (index > -1) {
matchers.splice(index, 1);
if (matcherRef.record.name) matcherMap.delete(matcherRef.record.name);
matcherRef.children.forEach(removeRoute);
matcherRef.alias.forEach(removeRoute);
}
}
}
function getRoutes() {
return matchers;
}
function insertMatcher(matcher) {
const index = findInsertionIndex(matcher, matchers);
matchers.splice(index, 0, matcher);
if (matcher.record.name && !isAliasRecord(matcher)) matcherMap.set(matcher.record.name, matcher);
}
function resolve(location$1, currentLocation) {
let matcher;
let params = {};
let path;
let name;
if ("name" in location$1 && location$1.name) {
matcher = matcherMap.get(location$1.name);
if (!matcher) throw createRouterError(ErrorTypes.MATCHER_NOT_FOUND, { location: location$1 });
if (true) {
const invalidParams = Object.keys(location$1.params || {}).filter((paramName) => !matcher.keys.find((k) => k.name === paramName));
if (invalidParams.length) warn$1(`Discarded invalid param(s) "${invalidParams.join('", "')}" when navigating. See https://github.com/vuejs/router/blob/main/packages/router/CHANGELOG.md#414-2022-08-22 for more details.`);
}
name = matcher.record.name;
params = assign(pickParams(currentLocation.params, matcher.keys.filter((k) => !k.optional).concat(matcher.parent ? matcher.parent.keys.filter((k) => k.optional) : []).map((k) => k.name)), location$1.params && pickParams(location$1.params, matcher.keys.map((k) => k.name)));
path = matcher.stringify(params);
} else if (location$1.path != null) {
path = location$1.path;
if (!path.startsWith("/")) warn$1(`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://github.com/vuejs/router/issues/new/choose.`);
matcher = matchers.find((m) => m.re.test(path));
if (matcher) {
params = matcher.parse(path);
name = matcher.record.name;
}
} else {
matcher = currentLocation.name ? matcherMap.get(currentLocation.name) : matchers.find((m) => m.re.test(currentLocation.path));
if (!matcher) throw createRouterError(ErrorTypes.MATCHER_NOT_FOUND, {
location: location$1,
currentLocation
});
name = matcher.record.name;
params = assign({}, currentLocation.params, location$1.params);
path = matcher.stringify(params);
}
const matched = [];
let parentMatcher = matcher;
while (parentMatcher) {
matched.unshift(parentMatcher.record);
parentMatcher = parentMatcher.parent;
}
return {
name,
path,
params,
matched,
meta: mergeMetaFields(matched)
};
}
routes.forEach((route) => addRoute(route));
function clearRoutes() {
matchers.length = 0;
matcherMap.clear();
}
return {
addRoute,
resolve,
removeRoute,
clearRoutes,
getRoutes,
getRecordMatcher
};
}
function pickParams(params, keys) {
const newParams = {};
for (const key of keys) if (key in params) newParams[key] = params[key];
return newParams;
}
function normalizeRouteRecord(record) {
const normalized = {
path: record.path,
redirect: record.redirect,
name: record.name,
meta: record.meta || {},
aliasOf: record.aliasOf,
beforeEnter: record.beforeEnter,
props: normalizeRecordProps(record),
children: record.children || [],
instances: {},
leaveGuards: /* @__PURE__ */ new Set(),
updateGuards: /* @__PURE__ */ new Set(),
enterCallbacks: {},
components: "components" in record ? record.components || null : record.component && { default: record.component }
};
Object.defineProperty(normalized, "mods", { value: {} });
return normalized;
}
function normalizeRecordProps(record) {
const propsObject = {};
const props = record.props || false;
if ("component" in record) propsObject.default = props;
else for (const name in record.components) propsObject[name] = typeof props === "object" ? props[name] : props;
return propsObject;
}
function isAliasRecord(record) {
while (record) {
if (record.record.aliasOf) return true;
record = record.parent;
}
return false;
}
function mergeMetaFields(matched) {
return matched.reduce((meta, record) => assign(meta, record.meta), {});
}
function isSameParam(a, b) {
return a.name === b.name && a.optional === b.optional && a.repeatable === b.repeatable;
}
function checkSameParams(a, b) {
for (const key of a.keys) if (!key.optional && !b.keys.find(isSameParam.bind(null, key))) return warn$1(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`);
for (const key of b.keys) if (!key.optional && !a.keys.find(isSameParam.bind(null, key))) return warn$1(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`);
}
function checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent) {
if (parent && parent.record.name && !mainNormalizedRecord.name && !mainNormalizedRecord.path) warn$1(`The route named "${String(parent.record.name)}" has a child without a name and an empty path. Using that name won't render the empty path child so you probably want to move the name to the child instead. If this is intentional, add a name to the child route to remove the warning.`);
}
function checkSameNameAsAncestor(record, parent) {
for (let ancestor = parent; ancestor; ancestor = ancestor.parent) if (ancestor.record.name === record.name) throw new Error(`A route named "${String(record.name)}" has been added as a ${parent === ancestor ? "child" : "descendant"} of a route with the same name. Route names must be unique and a nested route cannot use the same name as an ancestor.`);
}
function checkMissingParamsInAbsolutePath(record, parent) {
for (const key of parent.keys) if (!record.keys.find(isSameParam.bind(null, key))) return warn$1(`Absolute path "${record.record.path}" must have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
}
function findInsertionIndex(matcher, matchers) {
let lower = 0;
let upper = matchers.length;
while (lower !== upper) {
const mid = lower + upper >> 1;
if (comparePathParserScore(matcher, matchers[mid]) < 0) upper = mid;
else lower = mid + 1;
}
const insertionAncestor = getInsertionAncestor(matcher);
if (insertionAncestor) {
upper = matchers.lastIndexOf(insertionAncestor, upper - 1);
if (upper < 0) warn$1(`Finding ancestor route "${insertionAncestor.record.path}" failed for "${matcher.record.path}"`);
}
return upper;
}
function getInsertionAncestor(matcher) {
let ancestor = matcher;
while (ancestor = ancestor.parent) if (isMatchable(ancestor) && comparePathParserScore(matcher, ancestor) === 0) return ancestor;
}
function isMatchable({ record }) {
return !!(record.name || record.components && Object.keys(record.components).length || record.redirect);
}
function useLink(props) {
const router = inject(routerKey);
const currentRoute = inject(routeLocationKey);
let hasPrevious = false;
let previousTo = null;
const route = computed(() => {
const to = unref(props.to);
if (!hasPrevious || to !== previousTo) {
if (!isRouteLocation(to)) if (hasPrevious) warn$1(`Invalid value for prop "to" in useLink()
- to:`, to, `
- previous to:`, previousTo, `
- props:`, props);
else warn$1(`Invalid value for prop "to" in useLink()
- to:`, to, `
- props:`, props);
previousTo = to;
hasPrevious = true;
}
return router.resolve(to);
});
const activeRecordIndex = computed(() => {
const { matched } = route.value;
const { length } = matched;
const routeMatched = matched[length - 1];
const currentMatched = currentRoute.matched;
if (!routeMatched || !currentMatched.length) return -1;
const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched));
if (index > -1) return index;
const parentRecordPath = getOriginalPath(matched[length - 2]);
return length > 1 && getOriginalPath(routeMatched) === parentRecordPath && currentMatched[currentMatched.length - 1].path !== parentRecordPath ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2])) : index;
});
const isActive = computed(() => activeRecordIndex.value > -1 && includesParams(currentRoute.params, route.value.params));
const isExactActive = computed(() => activeRecordIndex.value > -1 && activeRecordIndex.value === currentRoute.matched.length - 1 && isSameRouteLocationParams(currentRoute.params, route.value.params));
function navigate(e = {}) {
if (guardEvent(e)) {
const p = router[unref(props.replace) ? "replace" : "push"](unref(props.to)).catch(noop);
if (props.viewTransition && typeof document !== "undefined" && "startViewTransition" in document) document.startViewTransition(() => p);
return p;
}
return Promise.resolve();
}
if (isBrowser) {
const instance = getCurrentInstance();
if (instance) {
const linkContextDevtools = {
route: route.value,
isActive: isActive.value,
isExactActive: isExactActive.value,
error: null
};
instance.__vrl_devtools = instance.__vrl_devtools || [];
instance.__vrl_devtools.push(linkContextDevtools);
watchEffect(() => {
linkContextDevtools.route = route.value;
linkContextDevtools.isActive = isActive.value;
linkContextDevtools.isExactActive = isExactActive.value;
linkContextDevtools.error = isRouteLocation(unref(props.to)) ? null : 'Invalid "to" value';
}, { flush: "post" });
}
}
return {
route,
href: computed(() => route.value.href),
isActive,
isExactActive,
navigate
};
}
function preferSingleVNode(vnodes) {
return vnodes.length === 1 ? vnodes[0] : vnodes;
}
var RouterLinkImpl = defineComponent({
name: "RouterLink",
compatConfig: { MODE: 3 },
props: {
to: {
type: [String, Object],
required: true
},
replace: Boolean,
activeClass: String,
exactActiveClass: String,
custom: Boolean,
ariaCurrentValue: {
type: String,
default: "page"
},
viewTransition: Boolean
},
useLink,
setup(props, { slots }) {
const link = reactive(useLink(props));
const { options } = inject(routerKey);
const elClass = computed(() => ({
[getLinkClass(props.activeClass, options.linkActiveClass, "router-link-active")]: link.isActive,
[getLinkClass(props.exactActiveClass, options.linkExactActiveClass, "router-link-exact-active")]: link.isExactActive
}));
return () => {
const children = slots.default && preferSingleVNode(slots.default(link));
return props.custom ? children : h("a", {
"aria-current": link.isExactActive ? props.ariaCurrentValue : null,
href: link.href,
onClick: link.navigate,
class: elClass.value
}, children);
};
}
});
var RouterLink = RouterLinkImpl;
function guardEvent(e) {
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
if (e.defaultPrevented) return;
if (e.button !== void 0 && e.button !== 0) return;
if (e.currentTarget && e.currentTarget.getAttribute) {
const target = e.currentTarget.getAttribute("target");
if (/\b_blank\b/i.test(target)) return;
}
if (e.preventDefault) e.preventDefault();
return true;
}
function includesParams(outer, inner) {
for (const key in inner) {
const innerValue = inner[key];
const outerValue = outer[key];
if (typeof innerValue === "string") {
if (innerValue !== outerValue) return false;
} else if (!isArray(outerValue) || outerValue.length !== innerValue.length || innerValue.some((value, i) => value !== outerValue[i])) return false;
}
return true;
}
function getOriginalPath(record) {
return record ? record.aliasOf ? record.aliasOf.path : record.path : "";
}
var getLinkClass = (propClass, globalClass, defaultClass) => propClass != null ? propClass : globalClass != null ? globalClass : defaultClass;
var RouterViewImpl = defineComponent({
name: "RouterView",
inheritAttrs: false,
props: {
name: {
type: String,
default: "default"
},
route: Object
},
compatConfig: { MODE: 3 },
setup(props, { attrs, slots }) {
warnDeprecatedUsage();
const injectedRoute = inject(routerViewLocationKey);
const routeToDisplay = computed(() => props.route || injectedRoute.value);
const injectedDepth = inject(viewDepthKey, 0);
const depth = computed(() => {
let initialDepth = unref(injectedDepth);
const { matched } = routeToDisplay.value;
let matchedRoute;
while ((matchedRoute = matched[initialDepth]) && !matchedRoute.components) initialDepth++;
return initialDepth;
});
const matchedRouteRef = computed(() => routeToDisplay.value.matched[depth.value]);
provide(viewDepthKey, computed(() => depth.value + 1));
provide(matchedRouteKey, matchedRouteRef);
provide(routerViewLocationKey, routeToDisplay);
const viewRef = ref();
watch(() => [
viewRef.value,
matchedRouteRef.value,
props.name
], ([instance, to, name], [oldInstance, from, oldName]) => {
if (to) {
to.instances[name] = instance;
if (from && from !== to && instance && instance === oldInstance) {
if (!to.leaveGuards.size) to.leaveGuards = from.leaveGuards;
if (!to.updateGuards.size) to.updateGuards = from.updateGuards;
}
}
if (instance && to && (!from || !isSameRouteRecord(to, from) || !oldInstance)) (to.enterCallbacks[name] || []).forEach((callback) => callback(instance));
}, { flush: "post" });
return () => {
const route = routeToDisplay.value;
const currentName = props.name;
const matchedRoute = matchedRouteRef.value;
const ViewComponent = matchedRoute && matchedRoute.components[currentName];
if (!ViewComponent) return normalizeSlot(slots.default, {
Component: ViewComponent,
route
});
const routePropsOption = matchedRoute.props[currentName];
const routeProps = routePropsOption ? routePropsOption === true ? route.params : typeof routePropsOption === "function" ? routePropsOption(route) : routePropsOption : null;
const onVnodeUnmounted = (vnode) => {
if (vnode.component.isUnmounted) matchedRoute.instances[currentName] = null;
};
const component = h(ViewComponent, assign({}, routeProps, attrs, {
onVnodeUnmounted,
ref: viewRef
}));
if (isBrowser && component.ref) {
const info = {
depth: depth.value,
name: matchedRoute.name,
path: matchedRoute.path,
meta: matchedRoute.meta
};
(isArray(component.ref) ? component.ref.map((r) => r.i) : [component.ref.i]).forEach((instance) => {
instance.__vrv_devtools = info;
});
}
return normalizeSlot(slots.default, {
Component: component,
route
}) || component;
};
}
});
function normalizeSlot(slot, data) {
if (!slot) return null;
const slotContent = slot(data);
return slotContent.length === 1 ? slotContent[0] : slotContent;
}
var RouterView = RouterViewImpl;
function warnDeprecatedUsage() {
const instance = getCurrentInstance();
const parentName = instance.parent && instance.parent.type.name;
const parentSubTreeType = instance.parent && instance.parent.subTree && instance.parent.subTree.type;
if (parentName && (parentName === "KeepAlive" || parentName.includes("Transition")) && typeof parentSubTreeType === "object" && parentSubTreeType.name === "RouterView") {
const comp = parentName === "KeepAlive" ? "keep-alive" : "transition";
warn$1(`<router-view> can no longer be used directly inside <transition> or <keep-alive>.
Use slot props instead:
<router-view v-slot="{ Component }">
<${comp}>
<component :is="Component" />
</${comp}>
</router-view>`);
}
}
function createRouter(options) {
const matcher = createRouterMatcher(options.routes, options);
const parseQuery$1 = options.parseQuery || parseQuery;
const stringifyQuery$1 = options.stringifyQuery || stringifyQuery;
const routerHistory = options.history;
if (!routerHistory) throw new Error('Provide the "history" option when calling "createRouter()": https://router.vuejs.org/api/interfaces/RouterOptions.html#history');
const beforeGuards = useCallbacks();
const beforeResolveGuards = useCallbacks();
const afterGuards = useCallbacks();
const currentRoute = shallowRef(START_LOCATION_NORMALIZED);
let pendingLocation = START_LOCATION_NORMALIZED;
if (isBrowser && options.scrollBehavior && "scrollRestoration" in history) history.scrollRestoration = "manual";
const normalizeParams = applyToParams.bind(null, (paramValue) => "" + paramValue);
const encodeParams = applyToParams.bind(null, encodeParam);
const decodeParams = applyToParams.bind(null, decode);
function addRoute(parentOrRoute, route) {
let parent;
let record;
if (isRouteName(parentOrRoute)) {
parent = matcher.getRecordMatcher(parentOrRoute);
if (!parent) warn$1(`Parent route "${String(parentOrRoute)}" not found when adding child route`, route);
record = route;
} else record = parentOrRoute;
return matcher.addRoute(record, parent);
}
function removeRoute(name) {
const recordMatcher = matcher.getRecordMatcher(name);
if (recordMatcher) matcher.removeRoute(recordMatcher);
else if (true) warn$1(`Cannot remove non-existent route "${String(name)}"`);
}
function getRoutes() {
return matcher.getRoutes().map((routeMatcher) => routeMatcher.record);
}
function hasRoute(name) {
return !!matcher.getRecordMatcher(name);
}
function resolve(rawLocation, currentLocation) {
currentLocation = assign({}, currentLocation || currentRoute.value);
if (typeof rawLocation === "string") {
const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path);
const matchedRoute$1 = matcher.resolve({ path: locationNormalized.path }, currentLocation);
const href$1 = routerHistory.createHref(locationNormalized.fullPath);
if (true) {
if (href$1.startsWith("//")) warn$1(`Location "${rawLocation}" resolved to "${href$1}". A resolved location cannot start with multiple slashes.`);
else if (!matchedRoute$1.matched.length) warn$1(`No match found for location with path "${rawLocation}"`);
}
return assign(locationNormalized, matchedRoute$1, {
params: decodeParams(matchedRoute$1.params),
hash: decode(locationNormalized.hash),
redirectedFrom: void 0,
href: href$1
});
}
if (!isRouteLocation(rawLocation)) {
warn$1(`router.resolve() was passed an invalid location. This will fail in production.
- Location:`, rawLocation);
return resolve({});
}
let matcherLocation;
if (rawLocation.path != null) {
if ("params" in rawLocation && !("name" in rawLocation) && Object.keys(rawLocation.params).length) warn$1(`Path "${rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`);
matcherLocation = assign({}, rawLocation, { path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path });
} else {
const targetParams = assign({}, rawLocation.params);
for (const key in targetParams) if (targetParams[key] == null) delete targetParams[key];
matcherLocation = assign({}, rawLocation, { params: encodeParams(targetParams) });
currentLocation.params = encodeParams(currentLocation.params);
}
const matchedRoute = matcher.resolve(matcherLocation, currentLocation);
const hash = rawLocation.hash || "";
if (hash && !hash.startsWith("#")) warn$1(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
hash: encodeHash(hash),
path: matchedRoute.path
}));
const href = routerHistory.createHref(fullPath);
if (true) {
if (href.startsWith("//")) warn$1(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
else if (!matchedRoute.matched.length) warn$1(`No match found for location with path "${rawLocation.path != null ? rawLocation.path : rawLocation}"`);
}
return assign({
fullPath,
hash,
query: stringifyQuery$1 === stringifyQuery ? normalizeQuery(rawLocation.query) : rawLocation.query || {}
}, matchedRoute, {
redirectedFrom: void 0,
href
});
}
function locationAsObject(to) {
return typeof to === "string" ? parseURL(parseQuery$1, to, currentRoute.value.path) : assign({}, to);
}
function checkCanceledNavigation(to, from) {
if (pendingLocation !== to) return createRouterError(ErrorTypes.NAVIGATION_CANCELLED, {
from,
to
});
}
function push(to) {
return pushWithRedirect(to);
}
function replace(to) {
return push(assign(locationAsObject(to), { replace: true }));
}
function handleRedirectRecord(to, from) {
const lastMatched = to.matched[to.matched.length - 1];
if (lastMatched && lastMatched.redirect) {
const { redirect } = lastMatched;
let newTargetLocation = typeof redirect === "function" ? redirect(to, from) : redirect;
if (typeof newTargetLocation === "string") {
newTargetLocation = newTargetLocation.includes("?") || newTargetLocation.includes("#") ? newTargetLocation = locationAsObject(newTargetLocation) : { path: newTargetLocation };
newTargetLocation.params = {};
}
if (newTargetLocation.path == null && !("name" in newTargetLocation)) {
warn$1(`Invalid redirect found:
${JSON.stringify(newTargetLocation, null, 2)}
when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`);
throw new Error("Invalid redirect");
}
return assign({
query: to.query,
hash: to.hash,
params: newTargetLocation.path != null ? {} : to.params
}, newTargetLocation);
}
}
function pushWithRedirect(to, redirectedFrom) {
const targetLocation = pendingLocation = resolve(to);
const from = currentRoute.value;
const data = to.state;
const force = to.force;
const replace$1 = to.replace === true;
const shouldRedirect = handleRedirectRecord(targetLocation, from);
if (shouldRedirect) return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
state: typeof shouldRedirect === "object" ? assign({}, data, shouldRedirect.state) : data,
force,
replace: replace$1
}), redirectedFrom || targetLocation);
const toLocation = targetLocation;
toLocation.redirectedFrom = redirectedFrom;
let failure;
if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
failure = createRouterError(ErrorTypes.NAVIGATION_DUPLICATED, {
to: toLocation,
from
});
handleScroll(from, from, true, false);
}
return (failure ? Promise.resolve(failure) : navigate(toLocation, from)).catch((error) => isNavigationFailure(error) ? isNavigationFailure(error, ErrorTypes.NAVIGATION_GUARD_REDIRECT) ? error : markAsReady(error) : triggerError(error, toLocation, from)).then((failure$1) => {
if (failure$1) {
if (isNavigationFailure(failure$1, ErrorTypes.NAVIGATION_GUARD_REDIRECT)) {
if (isSameRouteLocation(stringifyQuery$1, resolve(failure$1.to), toLocation) && redirectedFrom && (redirectedFrom._count = redirectedFrom._count ? redirectedFrom._count + 1 : 1) > 30) {
warn$1(`Detected a possibly infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow.
Are you always returning a new location within a navigation guard? That would lead to this error. Only return when redirecting or aborting, that should fix this. This might break in production if not fixed.`);
return Promise.reject(new Error("Infinite redirect in navigation guard"));
}
return pushWithRedirect(assign({ replace: replace$1 }, locationAsObject(failure$1.to), {
state: typeof failure$1.to === "object" ? assign({}, data, failure$1.to.state) : data,
force
}), redirectedFrom || toLocation);
}
} else failure$1 = finalizeNavigation(toLocation, from, true, replace$1, data);
triggerAfterEach(toLocation, from, failure$1);
return failure$1;
});
}
function checkCanceledNavigationAndReject(to, from) {
const error = checkCanceledNavigation(to, from);
return error ? Promise.reject(error) : Promise.resolve();
}
function runWithContext(fn) {
const app = installedApps.values().next().value;
return app && typeof app.runWithContext === "function" ? app.runWithContext(fn) : fn();
}
function navigate(to, from) {
let guards;
const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from);
guards = extractComponentsGuards(leavingRecords.reverse(), "beforeRouteLeave", to, from);
for (const record of leavingRecords) record.leaveGuards.forEach((guard) => {
guards.push(guardToPromiseFn(guard, to, from));
});
const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from);
guards.push(canceledNavigationCheck);
return runGuardQueue(guards).then(() => {
guards = [];
for (const guard of beforeGuards.list()) guards.push(guardToPromiseFn(guard, to, from));
guards.push(canceledNavigationCheck);
return runGuardQueue(guards);
}).then(() => {
guards = extractComponentsGuards(updatingRecords, "beforeRouteUpdate", to, from);
for (const record of updatingRecords) record.updateGuards.forEach((guard) => {
guards.push(guardToPromiseFn(guard, to, from));
});
guards.push(canceledNavigationCheck);
return runGuardQueue(guards);
}).then(() => {
guards = [];
for (const record of enteringRecords) if (record.beforeEnter) if (isArray(record.beforeEnter)) for (const beforeEnter of record.beforeEnter) guards.push(guardToPromiseFn(beforeEnter, to, from));
else guards.push(guardToPromiseFn(record.beforeEnter, to, from));
guards.push(canceledNavigationCheck);
return runGuardQueue(guards);
}).then(() => {
to.matched.forEach((record) => record.enterCallbacks = {});
guards = extractComponentsGuards(enteringRecords, "beforeRouteEnter", to, from, runWithContext);
guards.push(canceledNavigationCheck);
return runGuardQueue(guards);
}).then(() => {
guards = [];
for (const guard of beforeResolveGuards.list()) guards.push(guardToPromiseFn(guard, to, from));
guards.push(canceledNavigationCheck);
return runGuardQueue(guards);
}).catch((err) => isNavigationFailure(err, ErrorTypes.NAVIGATION_CANCELLED) ? err : Promise.reject(err));
}
function triggerAfterEach(to, from, failure) {
afterGuards.list().forEach((guard) => runWithContext(() => guard(to, from, failure)));
}
function finalizeNavigation(toLocation, from, isPush, replace$1, data) {
const error = checkCanceledNavigation(toLocation, from);
if (error) return error;
const isFirstNavigation = from === START_LOCATION_NORMALIZED;
const state = !isBrowser ? {} : history.state;
if (isPush) if (replace$1 || isFirstNavigation) routerHistory.replace(toLocation.fullPath, assign({ scroll: isFirstNavigation && state && state.scroll }, data));
else routerHistory.push(toLocation.fullPath, data);
currentRoute.value = toLocation;
handleScroll(toLocation, from, isPush, isFirstNavigation);
markAsReady();
}
let removeHistoryListener;
function setupListeners() {
if (removeHistoryListener) return;
removeHistoryListener = routerHistory.listen((to, _from, info) => {
if (!router.listening) return;
const toLocation = resolve(to);
const shouldRedirect = handleRedirectRecord(toLocation, router.currentRoute.value);
if (shouldRedirect) {
pushWithRedirect(assign(shouldRedirect, {
replace: true,
force: true
}), toLocation).catch(noop);
return;
}
pendingLocation = toLocation;
const from = currentRoute.value;
if (isBrowser) saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition());
navigate(toLocation, from).catch((error) => {
if (isNavigationFailure(error, ErrorTypes.NAVIGATION_ABORTED | ErrorTypes.NAVIGATION_CANCELLED)) return error;
if (isNavigationFailure(error, ErrorTypes.NAVIGATION_GUARD_REDIRECT)) {
pushWithRedirect(assign(locationAsObject(error.to), { force: true }), toLocation).then((failure) => {
if (isNavigationFailure(failure, ErrorTypes.NAVIGATION_ABORTED | ErrorTypes.NAVIGATION_DUPLICATED) && !info.delta && info.type === NavigationType.pop) routerHistory.go(-1, false);
}).catch(noop);
return Promise.reject();
}
if (info.delta) routerHistory.go(-info.delta, false);
return triggerError(error, toLocation, from);
}).then((failure) => {
failure = failure || finalizeNavigation(toLocation, from, false);
if (failure) {
if (info.delta && !isNavigationFailure(failure, ErrorTypes.NAVIGATION_CANCELLED)) routerHistory.go(-info.delta, false);
else if (info.type === NavigationType.pop && isNavigationFailure(failure, ErrorTypes.NAVIGATION_ABORTED | ErrorTypes.NAVIGATION_DUPLICATED)) routerHistory.go(-1, false);
}
triggerAfterEach(toLocation, from, failure);
}).catch(noop);
});
}
let readyHandlers = useCallbacks();
let errorListeners = useCallbacks();
let ready;
function triggerError(error, to, from) {
markAsReady(error);
const list = errorListeners.list();
if (list.length) list.forEach((handler) => handler(error, to, from));
else {
if (true) warn$1("uncaught error during route navigation:");
console.error(error);
}
return Promise.reject(error);
}
function isReady() {
if (ready && currentRoute.value !== START_LOCATION_NORMALIZED) return Promise.resolve();
return new Promise((resolve$1, reject) => {
readyHandlers.add([resolve$1, reject]);
});
}
function markAsReady(err) {
if (!ready) {
ready = !err;
setupListeners();
readyHandlers.list().forEach(([resolve$1, reject]) => err ? reject(err) : resolve$1());
readyHandlers.reset();
}
return err;
}
function handleScroll(to, from, isPush, isFirstNavigation) {
const { scrollBehavior } = options;
if (!isBrowser || !scrollBehavior) return Promise.resolve();
const scrollPosition = !isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0)) || (isFirstNavigation || !isPush) && history.state && history.state.scroll || null;
return nextTick().then(() => scrollBehavior(to, from, scrollPosition)).then((position) => position && scrollToPosition(position)).catch((err) => triggerError(err, to, from));
}
const go = (delta) => routerHistory.go(delta);
let started;
const installedApps = /* @__PURE__ */ new Set();
const router = {
currentRoute,
listening: true,
addRoute,
removeRoute,
clearRoutes: matcher.clearRoutes,
hasRoute,
getRoutes,
resolve,
options,
push,
replace,
go,
back: () => go(-1),
forward: () => go(1),
beforeEach: beforeGuards.add,
beforeResolve: beforeResolveGuards.add,
afterEach: afterGuards.add,
onError: errorListeners.add,
isReady,
install(app) {
app.component("RouterLink", RouterLink);
app.component("RouterView", RouterView);
app.config.globalProperties.$router = router;
Object.defineProperty(app.config.globalProperties, "$route", {
enumerable: true,
get: () => unref(currentRoute)
});
if (isBrowser && !started && currentRoute.value === START_LOCATION_NORMALIZED) {
started = true;
push(routerHistory.location).catch((err) => {
if (true) warn$1("Unexpected error when starting the router:", err);
});
}
const reactiveRoute = {};
for (const key in START_LOCATION_NORMALIZED) Object.defineProperty(reactiveRoute, key, {
get: () => currentRoute.value[key],
enumerable: true
});
app.provide(routerKey, router);
app.provide(routeLocationKey, shallowReactive(reactiveRoute));
app.provide(routerViewLocationKey, currentRoute);
const unmountApp = app.unmount;
installedApps.add(app);
app.unmount = function() {
installedApps.delete(app);
if (installedApps.size < 1) {
pendingLocation = START_LOCATION_NORMALIZED;
removeHistoryListener && removeHistoryListener();
removeHistoryListener = null;
currentRoute.value = START_LOCATION_NORMALIZED;
started = false;
ready = false;
}
unmountApp();
};
if (isBrowser) addDevtools(app, router, matcher);
}
};
function runGuardQueue(guards) {
return guards.reduce((promise, guard) => promise.then(() => runWithContext(guard)), Promise.resolve());
}
return router;
}
function useRouter() {
return inject(routerKey);
}
function useRoute(_name) {
return inject(routeLocationKey);
}
export {
NavigationFailureType,
RouterLink,
RouterView,
START_LOCATION_NORMALIZED as START_LOCATION,
createMemoryHistory,
createRouter,
createRouterMatcher,
createWebHashHistory,
createWebHistory,
isNavigationFailure,
loadRouteLocation,
matchedRouteKey,
onBeforeRouteLeave,
onBeforeRouteUpdate,
parseQuery,
routeLocationKey,
routerKey,
routerViewLocationKey,
stringifyQuery,
useLink,
useRoute,
useRouter,
viewDepthKey
};
//# sourceMappingURL=vue-router.js.map
This diff could not be displayed because it is too large.
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-YHMWYXEE.js";
import "./chunk-G3PMV62Z.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}
......@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>商机办结登记</title>
</head>
<body>
<div id="app"></div>
......
......@@ -18,6 +18,7 @@
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@tailwindcss/typography": "^0.5.19",
"axios": "^1.13.2",
"date-fns": "^4.1.0",
"element-plus": "^2.11.7",
"lucide-vue-next": "^0.552.0",
......
{
"c": 0,
"d": {
"areaId": 2,
"functions": [
{
"id": 1,
"name": "业务办理",
"code": null,
"type": 1,
"parentId": 0,
"sortOrder": 0,
"apiPath": null,
"status": 1,
"children": [
{
"id": 2,
"name": "登记订单管理",
"code": null,
"type": 1,
"parentId": 1,
"sortOrder": 1,
"apiPath": null,
"status": 1,
"children": [
{
"id": 3,
"name": "登记订单列表",
"code": "ORDER_LIST",
"type": 2,
"parentId": 2,
"sortOrder": 1,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 4,
"name": "修改订单",
"code": "ORDER_UPDATE_MONEY",
"type": 2,
"parentId": 2,
"sortOrder": 2,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 5,
"name": "审核订单",
"code": "ORDER_AUDIT",
"type": 2,
"parentId": 2,
"sortOrder": 3,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 6,
"name": "关闭订单",
"code": "ORDER_CLOSED",
"type": 2,
"parentId": 2,
"sortOrder": 4,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 7,
"name": "导出订单",
"code": "ORDER_EXPORT",
"type": 2,
"parentId": 2,
"sortOrder": 5,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 23,
"name": "修改CRM订单号",
"code": "ORDER_UPDATE_CRM_ORDER",
"type": 2,
"parentId": 2,
"sortOrder": 6,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 24,
"name": "批量上传订单金额Excel",
"code": "ORDER_READ_MODIFY_EXCEL",
"type": 2,
"parentId": 2,
"sortOrder": 7,
"apiPath": null,
"status": 1,
"children": []
}
]
},
{
"id": 8,
"name": "业务酬金管理",
"code": null,
"type": 1,
"parentId": 1,
"sortOrder": 2,
"apiPath": null,
"status": 1,
"children": [
{
"id": 9,
"name": "酬金列表",
"code": "JOB_LIST",
"type": 2,
"parentId": 8,
"sortOrder": 1,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 10,
"name": "酬金修改",
"code": "JOB_UPDATE",
"type": 2,
"parentId": 8,
"sortOrder": 2,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 11,
"name": "酬金删除",
"code": "JOB_DELETE",
"type": 2,
"parentId": 8,
"sortOrder": 3,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 12,
"name": "业务酬金批量上传",
"code": "JOB_READ_EXCEL",
"type": 2,
"parentId": 8,
"sortOrder": 4,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 13,
"name": "酬金批量上传",
"code": "JOB_BATCH_UPLOAD",
"type": 2,
"parentId": 8,
"sortOrder": 5,
"apiPath": null,
"status": 1,
"children": []
}
]
}
]
},
{
"id": 14,
"name": "系统管理",
"code": null,
"type": 1,
"parentId": 0,
"sortOrder": 0,
"apiPath": null,
"status": 1,
"children": [
{
"id": 15,
"name": "角色管理",
"code": null,
"type": 1,
"parentId": 14,
"sortOrder": 1,
"apiPath": null,
"status": 1,
"children": [
{
"id": 16,
"name": "创建角色",
"code": "ROLE_CREATE",
"type": 2,
"parentId": 15,
"sortOrder": 1,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 17,
"name": "修改角色",
"code": "ROLE_UPDATE",
"type": 2,
"parentId": 15,
"sortOrder": 2,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 18,
"name": "角色列表",
"code": "ROLE_LIST",
"type": 2,
"parentId": 15,
"sortOrder": 3,
"apiPath": null,
"status": 1,
"children": []
}
]
},
{
"id": 19,
"name": "账号管理",
"code": null,
"type": 1,
"parentId": 14,
"sortOrder": 1,
"apiPath": null,
"status": 1,
"children": [
{
"id": 20,
"name": "创建用户",
"code": "USER_CREATE",
"type": 2,
"parentId": 19,
"sortOrder": 2,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 21,
"name": "修改用户",
"code": "USER_UPDATE",
"type": 2,
"parentId": 19,
"sortOrder": 3,
"apiPath": null,
"status": 1,
"children": []
},
{
"id": 22,
"name": "用户列表",
"code": "USER_LIST",
"type": 2,
"parentId": 19,
"sortOrder": 4,
"apiPath": null,
"status": 1,
"children": []
}
]
}
]
}
],
"nickname": "济南超级管理员",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE4NTAwMjA4NzgsInVzZXIiOiJ7XCJyb2xlSWRcIjowLFwidXNlcklkXCI6MX0iLCJpYXQiOjE3NjM2MjA4Nzh9.dgiaMJJtMTTI38Wc_VoVOPqO_Xm9LqGrqE8Bo15pjlk"
},
"m": "成功"
}
\ No newline at end of file
......@@ -4,7 +4,7 @@ import LoginPage from './components/LoginPage.vue'
import DesktopMain from './components/DesktopMain.vue'
// 登录状态管理
const isLoggedIn = ref(false)
const isLoggedIn = ref(localStorage.getItem('pcUserInfo')?true:false)
const currentUser = ref<{ username: string; role: 'admin' | 'viewer' } | null>(null)
// 登录处理
......@@ -13,8 +13,17 @@ const handleLogin = (username: string, role: 'admin' | 'viewer') => {
isLoggedIn.value = true
}
if(isLoggedIn){
const userInfo = JSON.parse(localStorage.getItem('pcUserInfo') || '{}')
const name = userInfo?.nickname || ''
if(name) {
handleLogin(name, 'admin')
}
}
// 登出处理
const handleLogout = () => {
localStorage.removeItem('pcUserInfo')
currentUser.value = null
isLoggedIn.value = false
}
......
import request from '../../request'
/**
* 查询账号列表
*/
export function queryAccountList(data) {
return request({
url: '/crm/getUserPageList',
data,
})
}
/**
* 添加/更新账号
*/
export function addAndUpdateRole(data) {
return request({
url: '/crm' + (data.id?'/updateUser':'/createUser'),
data,
})
}
/**
* 获取指定区域下的所有区域
*/
export function queryAreaData(data) {
return request({
url: '/crm/getArea?areaId='+data.areaId,
method: 'GET'
})
}
\ No newline at end of file
import request from '../../request'
/**
* 根据级别查询区域列表
* 1-省级,2-市级,3-区级,4-网格级
*/
export function queryLevelAllArea(data) {
return request({
url: '/compass/api/common/areas/level?areaLevel='+data.areaLevel+'&parentAreaCode='+data.parentAreaCode,
method: 'GET'
})
}
/**
* 当前用户权限获取下级区域层级结构
*/
export function queryUserArea(data) {
return request({
url: '/compass/api/system/area/permission/hierarchy',
data,
})
}
/**
* 商机状态列表
*/
export function queryBusiStatus(data) {
return request({
url: '/compass/api/common/enums/opportunity-statuses',
method: 'GET',
data,
})
}
\ No newline at end of file
import * as account from './account'
import * as common from './common'
import * as login from './login'
import * as order from './order'
import * as reward from './reward'
import * as role from './role'
export default {
...account,
...common,
...login,
...order,
...reward,
...role
}
\ No newline at end of file
import request from '../../request'
/**
* 退出登录
*/
export function logout() {
return request({
url: '/compass/api/auth/logout',
data: {}
})
}
/**
* 手机账号登录
*/
export function pohoneLogin(data) {
return request({
url: '/crm/login',
data,
})
}
/**
* 获取图形验证码
* @param loginName
* @param password
*/
export function getImgCode(data) {
return request({
url: '/crm/getCode',
method: 'GET',
data,
})
}
/**
* 获取短信验证码
* @param loginName
* @param password
*/
export function getTelCode(data) {
return request({
url: '/crm/sendMessage',
data,
})
}
import request from '../../request'
/**
* 订单列表查询
* @returns {AxiosPromise}
*/
export function queryOrderList(data) {
return request({
url: '/crm/getOrderList',
data,
})
}
/**
* 订单列表导出
* @returns {AxiosPromise}
*/
export function exportOrderList(data) {
return request({
url: '/crm/exportOrders',
data,
responseType: 'blob'
})
}
/**
* 订单审核
* @returns {AxiosPromise}
*/
export function audioOrderList(data) {
return request({
url: '/crm/auditOrder',
data,
})
}
/**
* 订单金额批量修改:表格上传
* @returns {AxiosPromise}
*/
export function updateOrderListMoney(data) {
return request({
url: '/crm/readExcelModifyOrder',
headers: {
'Content-Type': 'multipart/form-data'
},
data,
})
}
/**
* 订单金额修改
* @returns {AxiosPromise}
*/
export function updateOrderMoney(data) {
return request({
url: '/crm/updateOrderMoney',
data,
})
}
/**
* 订单crm修改
* @returns {AxiosPromise}
*/
export function updateOrderCrm(data) {
return request({
url: '/crm/updateCrmOrderId',
data,
})
}
/**
* 订单关闭
* @returns {AxiosPromise}
*/
export function closeOrder(data) {
return request({
url: '/crm/closeOrder',
data,
})
}
/**
* 订单日志
* @returns {AxiosPromise}
*/
export function queryOrderLog(data) {
return request({
url: '/crm/getOrderLog',
data,
})
}
\ No newline at end of file
import request from '../../request'
/**
* 业务酬金列表查询
*/
export function queryAllRewardList() {
return request({
url: '/crm/getJobsList',
method: 'GET'
})
}
/**
* 业务酬金批量上传:表格读取
*/
export function uploadReward(formData) {
return request({
url: '/crm/readExcelJobs',
headers: {
'Content-Type': 'multipart/form-data'
},
data: formData
})
}
/**
* 业务酬金批量上传
*/
export function uploadRewardSave(data) {
return request({
url: '/crm/batchUploadJobs',
data,
})
}
/**
* 业务酬金新增和修改
*/
export function updateReward(data) {
return request({
url: data.id?'/crm/updateJob':'/crm/addJobs',
data,
})
}
/**
* 业务酬金删除
*/
export function delReward(data) {
return request({
url: '/crm/delJob',
data,
})
}
\ No newline at end of file
import request from '../../request'
/**
* 查询角色列表
*/
export function queryRoleList(data) {
return request({
url: '/crm/getRoleList',
method: 'GET',
data,
})
}
/**
* 创建和修改角色
*/
export function createOrUpdateRole(data) {
let url = '/crm/createRole'
if(data.roleId){
url = '/crm/updateRole'
}
return request({
url,
data,
})
}
/**
* 根据角色ID获取角色权限
*/
export function queryRolePermission(data) {
return request({
url: '/crm/getRoleFunction',
data,
})
}
import axios from "axios";
import { ElMessageBox } from "element-plus";
import router from "@/router";
const service = axios.create({
baseURL: '/hallserver',
method: "post",
timeout: 150000,
withCredentials: true,
});
//请求拦截
service.interceptors.request.use(
(config) => {
if (!config.headers["Content-Type"])
config.headers["Content-Type"] = "application/json;charset=utf-8";
if (localStorage.pcUserInfo) {
let userInfo = JSON.parse(localStorage.pcUserInfo);
config.headers["token"] = userInfo.token
}
return config;
},
(error) => {
Promise.reject(error);
}
);
let ifCanShow = true; //为了防止页面有异常情况时,多个接口请求导致弹窗多次的问题
let catchFun = function (msg) {
if (!ifCanShow) {
return;
}
ifCanShow = false;
ElMessageBox.confirm(msg, "提示", {
showClose: false,
closeOnPressEscape: false,
closeOnClickModal: false,
showCancelButton: false,
}).then(() => {
ifCanShow = true;
router.push({ path: "/login" });
});
};
//响应拦截
service.interceptors.response.use(
(response) => {
console.log(response);
if (response.status == 200) {
if (response.data.code == "401") {
//登陆失效,重新登陆
catchFun("账户状态异常");
} else if (response.data.code == "133") {
//灰名单
ElMessageBox.alert(response.data.msg, "状态异常/错误提示", {
dangerouslyUseHTMLString: true,
});
} else {
if (response.data instanceof Blob) {
return new Promise(function (resolve, reject) {
var r = new FileReader();
var resData = response.data;
if (response.config.url.indexOf("poster/createPoster") >= 0) {
if (resData.type == "application/json") {
r.readAsText(resData);
} else {
r.readAsDataURL(resData);
}
} else {
r.readAsText(resData);
}
r.onload = function () {
let res = {};
//PK 为二进制压缩包(ZIP)导出数据流
if (
escape(r.result).indexOf("%u") == 0 ||
escape(r.result).indexOf("PK") == 0 ||
r.result.indexOf("pdf") >= 0 ||
r.result.indexOf("PDF") >= 0 ||
r.result.indexOf("data:image") >= 0
) {
res.type = "blob";
res.value = resData;
} else {
res.type = "object";
res.value = JSON.parse(r.result);
}
resolve(res);
};
}).catch((e) => {});
} else {
return {
url: response.config.url,
...response.data,
};
}
}
} else if (response.status == 302) {
catchFun("登陆失效,请重新登陆");
} else if (response.status == 401 || response.status == 403) {
catchFun("账户状态异常");
} else {
if (sessionStorage.notFirstIn) {
catchFun("网络异常,请稍后再试");
}
}
},
(error) => {
if (sessionStorage.notFirstIn) {
catchFun("网络异常,请稍后再试");
}
}
);
export default service;
import axios from 'axios'
import {
ElMessageBox
} from 'element-plus'
export default {
/**
* 数组元素查询——针对数组元素中object某一项值
* @param val
* @param store
* @param findKey
*/
arrayFind: function(val, store, findKey) {
let res = ''
if(!store) {
return val
}
if(!findKey) {
findKey = 'value'
}
try {
store.forEach(function(item, index) {
if(item[findKey] == val) {
let curItem = JSON.parse(JSON.stringify(item))
curItem.findIndex = index
res = curItem
throw new Error('end forEach')
}
})
} catch(e) {
if(e.message = 'end forEach') {
console.log(e.message)
}
}
return res
},
/**
* 枚举渲染
* @param ename
* @param store
* @returns {*}
*/
globalRender: function(ename, store, checkFlag) {
let val = ''
if (!checkFlag) {
val = ename
}
if (!store) {
return val
}
if (store.length != 0) {
for (let i = 0; i < store.length; i++) {
if (ename == store[i].key) {
return store[i].value || store[i].key
} else if (i == store.length - 1) {
return val;
}
}
} else {
return val;
}
},
/**
* 时间戳转字符串
* @param num
* @returns {*}
*/
detailTime: function(num) {
if (num == null) {
return '';
} else {
num = Number(num);
let d = new Date(num);
let year = d.getFullYear();
let month = d.getMonth() + 1;
let date = d.getDate();
let hour = d.getHours();
let minute = d.getMinutes();
let second = d.getSeconds();
month < 10 ? month = '0' + month : month;
date < 10 ? date = '0' + date : date;
hour < 10 ? hour = '0' + hour : hour;
minute < 10 ? minute = '0' + minute : minute;
second < 10 ? second = '0' + second : second;
return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
}
},
/**
* 文件后缀时间戳
* @param {*} num
* @returns
*/
fileTimeStamp: function(num) {
if (num == null) {
return '';
} else {
num = Number(num);
let d = new Date(num);
let year = d.getFullYear();
let month = d.getMonth() + 1;
let date = d.getDate();
let hour = d.getHours();
let minute = d.getMinutes();
let second = d.getSeconds();
month < 10 ? month = '0' + month : month;
date < 10 ? date = '0' + date : date;
hour < 10 ? hour = '0' + hour : hour;
minute < 10 ? minute = '0' + minute : minute;
second < 10 ? second = '0' + second : second;
return year + "-" + month + "-" + date + "-" + hour + "" + minute + "" + second;
}
},
/**
* 浮点数加法,用于解决精度丢失问题
* @param {*} num1
* @param {*} num2
* @returns
*/
addStr: function(num1, num2) {
var r1, r2, m;
try {
r1 = num1.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = num2.toString().split(".")[1].length;
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2));
return Math.round(num1 * m + num2 * m) / m;
},
/**
* 浮点数减法,用于解决精度丢失问题
* @param {*} arg1
* @param {*} arg2
* @returns
*/
subStr: function(arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split(".")[1].length
} catch (e) {
r1 = 0
}
try {
r2 = arg2.toString().split(".")[1].length
} catch (e) {
r2 = 0
}
m = Math.pow(10, Math.max(r1, r2));
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
},
/**
* 浮点数乘法,用于解决精度丢失问题
* @param {*} arg1
* @param {*} arg2
* @param {*} n 保留位数
* @returns
*/
mulStr: function(arg1, arg2, n) {
var m = 0,
s1 = arg1.toString(),
s2 = arg2.toString();
try {
m += s1.split('.')[1].length
} catch (e) {
//TODO handle the exception
}
try {
m += s2.split('.')[1].length
} catch (e) {}
var val = Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m)
if(n) {
val = val.toFixed(n)
}
return val
},
/**
* 浮点数除法,用于解决精度丢失问题
* @param {*} arg1
* @param {*} arg2
* @param {*} n 保留位数
* @returns
*/
divStr: function(arg1, arg2, n) {
var t1 = 0,
t2 = 0,
r1, r2;
try {
t1 = arg1.toString().split('.')[1].length
} catch (e) {
//TODO handle the exception
}
try {
t2 = arg2.toString().split('.')[1].length
} catch (e) {
//TODO handle the exception
}
r1 = Number(arg1.toString().replace('.', ''));
r2 = Number(arg2.toString().replace('.', ''));
var val = (r1 / r2) * Math.pow(10, t2 - t1)
if(n) {
val = val.toFixed(n)
}
return val
},
/**
* 隐藏身份证号
* @param {*} idCard
* @returns
*/
hideIdNumber: function(idCard) {
if (idCard.length == 18) {
return idCard.substr(0, 6) + '********' + idCard.substr(-4, 4)
} else if (idCard.length == 15) {
return idCard.substr(0, 6) + '******' + idCard.substr(-3, 3)
} else {
return idCard
}
},
/**
* 隐藏手机号
* @param {*} phone
* @returns
*/
hidePhone: function(phone) {
if(phone.length == 11) {
return phone.substr(0,3) + '*****' + phone.substr(-3,3)
} else {
return phone
}
},
/**
* 导出excel
* @param content
* @param fileName
* @param fileParam
* @param blobType
* @param fileType
* */
exportExcel: function({
content,
fileName,
fileParam,
blobType,
fileType
}) {
let __this = this
if (!content) return
if (!blobType) blobType = 'application/vnd.ms-excel'
if (!fileName) fileName = '导出文件'
if (!fileType) fileType = 'xls'
if (!fileParam) fileParam = ''
let fileNames = fileName + fileParam
__this.createBlob(content, blobType, fileNames, fileType)
},
/**
* 下载本地文件
* @param fileJson
* */
downloadLocalFile: function(fileSrc, blobType, fileName, fileType) {
let __this = this
let getUrl = fileSrc
if (getUrl.indexOf('?') < 0) getUrl = fileSrc + '?' + new Date().getTime()
console.log(getUrl)
axios.create().get(getUrl, {
responseType: 'blob'
}).then(response => {
if (window.navigator.msSaveBlob) { //ie浏览器不支持通过a标签下载文件
try {
window.navigator.msSaveBlob(response.data, (fileName + '-' + __this.fileTimeStamp(new Date().getTime()) +
'.' + fileType))
} catch (e) {
ElMessageBox.confirm('该浏览器内核不支持下载此文件,推荐使用谷歌浏览器访问本平台', '提示', {
confirmButtonText: "确定",
showClose: false,
showCancelButton: false,
callback: function(action) {
// window.location.reload()
}
})
}
} else {
__this.createBlob(response.data, blobType, fileName, fileType)
}
})
},
/**
* 创建blob,用于下载文件
* @param {*} content
* @param {*} blobType
* @param {*} fileName
* @param {*} fileType
*/
createBlob: function(content, blobType, fileName, fileType) {
let __this = this
let blob = new Blob([content], {
type: blobType
})
let filename = fileName + '-' + __this.fileTimeStamp(new Date().getTime()) + '.' + fileType
if ('download' in document.createElement('a')) {
let eleLink = document.createElement('a')
eleLink.download = filename
eleLink.style.display = 'none'
eleLink.href = URL.createObjectURL(blob)
document.body.appendChild(eleLink)
eleLink.click()
URL.revokeObjectURL(eleLink.href)
document.body.removeChild(eleLink)
} else {
navigator.msSaveBlob(blob, filename)
}
},
/**
* 通过url下载文件
* @param {*} url
*/
downloadFile(url) {
let urlParam = '';
if (url.indexOf('?') >= 0) {
urlParam = url.split('?')[1]
url = url.split('?')[0]
}
let splitArr = url.split('/')
let fileName = splitArr[splitArr.length - 1].slice(0, -4)
let fileType = splitArr[splitArr.length - 1].slice(-4)
if (fileType.indexOf('.') < 0) {
fileName = fileName.slice(0, -1)
} else {
fileType = fileType.replace(/./, '')
}
let lastType = fileType
fileType = fileType.toLowerCase()
console.log(fileName, fileType)
let blobType = ''
switch (fileType) {
case 'xls':
case 'xlsx':
blobType = 'application/vnd.ms-excel'
break;
// case 'xlsx':
// blobType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.'
// break;
case 'doc':
case 'docx':
blobType = 'application/msword'
break;
case 'ppt':
case 'pptx':
blobType = 'application/vnd.ms-powerpoint'
break;
case 'txt':
blobType = 'text/plain'
break;
case 'jpg':
case 'jpeg':
blobType = 'image/jpeg'
break;
case 'png':
blobType = 'image/png'
break;
case 'pdf':
blobType = 'application/pdf'
break;
case 'zip':
blobType = 'application/zip'
break;
}
let imgType = '.jpg,.jpeg,.png'
if (imgType.indexOf(fileType) >= 0) {
let image = new Image()
image.setAttribute("crossOrigin", "anonymous");
image.onload = function() {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
let url = canvas.toDataURL(blobType, 1.0); //得到图片的base64编码数据
let a = document.createElement("a"); // 生成一个a元素
let event = new MouseEvent("click"); // 创建一个单击事件
// let filename = fileName + '-' + fileTimeStamp(new Date().getTime()) + '.' + fileType
let filename = fileName + '.' + lastType
a.download = filename; // 设置图片名称
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
};
image.src = url;
} else {
let fileSrc = splitArr.slice(0, -1).join('/') + '/' + encodeURIComponent(fileName) + '.' + fileType
if (fileType == 'txt') {
console.log(fileSrc, url)
downloadLocalFile(fileSrc, blobType, fileName, fileType)
} else {
let form = document.createElement('form')
form.method = 'get'
form.action = urlParam ? (fileSrc + '?' + urlParam) : fileSrc
document.body.append(form)
form.submit()
form.remove()
}
}
}
}
\ No newline at end of file
const storesData = {}
export default storesData
\ No newline at end of file
......@@ -33,46 +33,14 @@
<input
ref="fileInputRef"
type="file"
accept=".csv"
accept=".csv,.xlsx"
@change="handleFileChange"
style="display: none"
/>
<!-- 筛选条件区 -->
<el-card class="p-5">
<div class="flex items-center gap-3 w-full">
<!-- 搜索框 -->
<div class="flex-1 relative">
<Search class="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="searchKeyword"
placeholder="搜索业务代码或业务名称..."
class="search-input"
clearable
/>
</div>
<!-- 查询按钮 -->
<div class="shrink-0">
<el-button
type="primary"
@click="handleSearch"
class="h-10"
>
查询
</el-button>
</div>
</div>
</el-card>
<!-- 业务规则表格 -->
<el-card class="p-5">
<div class="flex items-center justify-between mb-4">
<div class="text-sm text-neutral-600">
{{ filteredRules.length }} 条记录
</div>
</div>
<div class="rounded-lg overflow-hidden">
<el-table
:data="paginatedRules"
......@@ -81,35 +49,35 @@
:header-cell-style="{ backgroundColor: '#f3f4f6', color: '#374151', fontWeight: '500', borderBottom: '1px solid #e5e7eb' }"
row-class-name="hover:bg-muted/30"
>
<el-table-column prop="businessCode" label="业务代码" min-width="120">
<el-table-column prop="jobId" label="业务代码" min-width="120">
<template #default="{ row }">
<span class="font-mono">{{ row.businessCode }}</span>
<span class="font-mono">{{ row.jobId }}</span>
</template>
</el-table-column>
<el-table-column prop="businessName" label="业务名称" min-width="150" />
<el-table-column prop="jobName" label="业务名称" min-width="150" />
<el-table-column label="预计酬金(元)" min-width="120">
<template #default="{ row }">
¥{{ row.estimatedReward.toFixed(2) }}
¥{{ row.totalPrice.toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="业务状态" min-width="100">
<template #default="{ row }">
<el-tag
:type="row.status === '生效中' ? 'success' : 'info'"
:type="row.status === 1 ? 'success' : 'info'"
class="border-0"
>
{{ row.status }}
{{ row.status===1?'生效中':'已停用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="150">
<template #default="{ row }">
{{ row.createTime || '-' }}
{{ $utils.detailTime(row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="updateTime" label="更新时间" min-width="150">
<template #default="{ row }">
{{ row.updateTime || '-' }}
{{ $utils.detailTime(row.updateTime) }}
</template>
</el-table-column>
<el-table-column label="操作" min-width="200" fixed="right">
......@@ -122,12 +90,12 @@
>
编辑
</button>
<button
<!-- <button
@click="handleDeleteConfirm(row.id)"
class="text-brand-primary hover:text-brand-primary-hover bg-transparent border-none cursor-pointer text-sm font-normal p-0"
>
删除
</button>
</button> -->
</div>
</template>
</el-table-column>
......@@ -140,21 +108,6 @@
</template>
</el-table>
</div>
<!-- 分页组件 -->
<div v-if="totalPages > 1" class="flex items-center justify-between px-6 py-4">
<div class="text-sm text-neutral-600">
显示第 {{ startIndex + 1 }}-{{ Math.min(endIndex, filteredRules.length) }} 条,共 {{ filteredRules.length }} 条
</div>
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="filteredRules.length"
layout="prev, pager, next"
:pager-count="5"
small
/>
</div>
</el-card>
<!-- 新建/编辑业务规则对话框 -->
......@@ -172,6 +125,7 @@
</label>
<el-input
v-model="formData.businessCode"
:disabled="editingRule"
placeholder="请输入业务代码,如:B5G001"
/>
</div>
......@@ -181,6 +135,7 @@
</label>
<el-input
v-model="formData.businessName"
:disabled="editingRule"
placeholder="请输入业务名称,如:5G套餐办理"
/>
</div>
......@@ -200,8 +155,8 @@
<div>
<label class="block text-sm font-medium text-neutral-700 mb-2">状态</label>
<el-radio-group v-model="formData.status">
<el-radio label="生效中">启用</el-radio>
<el-radio label="已停用">停用</el-radio>
<el-radio label="1">生效</el-radio>
<el-radio label="0">停用</el-radio>
</el-radio-group>
</div>
</div>
......@@ -227,13 +182,13 @@
<div class="flex items-center gap-4 p-4 bg-neutral-50 rounded-lg">
<div class="flex items-center gap-2 flex-1">
<CheckCircle class="h-5 w-5 text-green-500" />
<span>成功导入:</span>
<span>正常</span>
<span class="text-green-500">{{ uploadResult?.success || 0 }}</span>
<span></span>
</div>
<div class="flex items-center gap-2 flex-1">
<AlertCircle class="h-5 w-5 text-red-500" />
<span>导入失败:</span>
<span>错误</span>
<span class="text-red-500">{{ uploadResult?.failed || 0 }}</span>
<span></span>
</div>
......@@ -249,38 +204,30 @@
>
{{ error }}
</div>
<div
v-if="uploadResult.errors.length >= 20"
class="text-sm text-neutral-500 italic"
>
仅显示前20条错误信息...
</div>
</div>
</div>
</div>
<template #footer>
<el-button type="primary" @click="showUploadResult = false">
确定
</el-button>
<el-button type="primary" @click="submitExcelData">{{(uploadResult?.failed || 0)>0?'确定':'提交'}}</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'
import { ref, computed, nextTick ,onMounted,getCurrentInstance} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Download, Upload, Plus, Search, CheckCircle, AlertCircle } from 'lucide-vue-next'
// import * as XLSX from 'xlsx' // 暂时注释掉,因为没有安装xlsx包
const { $api,$utils } = getCurrentInstance()!.appContext.config.globalProperties
// 业务规则类型定义
interface BusinessRule {
id: string
businessCode: string
businessName: string
estimatedReward: number
status: '生效中' | '已停用'
jobId: string
jobName: string
totalPrice: number
status: '1' | '0'
createTime?: string
updateTime?: string
}
......@@ -300,6 +247,23 @@ const props = withDefaults(defineProps<Props>(), {
rules: () => []
})
onMounted(() => {
queryRewardList()
})
const queryRewardList = async ()=>{
try {
const response = await $api.queryAllRewardList({})
if (response.c === 0 && response.d) {
paginatedRules.value = response.d
} else {
}
} catch (error) {
}
}
// Emits
const emit = defineEmits<{
add: [rule: Omit<BusinessRule, 'id' | 'createTime'>]
......@@ -322,24 +286,10 @@ const formData = ref({
businessCode: '',
businessName: '',
estimatedReward: 0,
status: '生效中' as '生效中' | '已停用'
status: '1'
})
// 计算属性
const filteredRules = computed(() => {
return props.rules.filter(rule => {
const matchesSearch = !searchKeyword.value ||
rule.businessCode.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
rule.businessName.toLowerCase().includes(searchKeyword.value.toLowerCase())
return matchesSearch
})
})
const totalPages = computed(() => Math.ceil(filteredRules.value.length / pageSize.value))
const startIndex = computed(() => (currentPage.value - 1) * pageSize.value)
const endIndex = computed(() => startIndex.value + pageSize.value)
const paginatedRules = computed(() => filteredRules.value.slice(startIndex.value, endIndex.value))
const paginatedRules = ref([])
// 方法
const handleSearch = () => {
......@@ -350,10 +300,10 @@ const handleOpenDialog = (rule?: BusinessRule) => {
if (rule) {
editingRule.value = rule
formData.value = {
businessCode: rule.businessCode,
businessName: rule.businessName,
estimatedReward: rule.estimatedReward,
status: rule.status
businessCode: rule.jobId,
businessName: rule.jobName,
estimatedReward: rule.totalPrice,
status: rule.status+''
}
} else {
editingRule.value = null
......@@ -361,7 +311,7 @@ const handleOpenDialog = (rule?: BusinessRule) => {
businessCode: '',
businessName: '',
estimatedReward: 0,
status: '生效中'
status: '1'
}
}
isDialogOpen.value = true
......@@ -374,30 +324,33 @@ const handleCloseDialog = () => {
businessCode: '',
businessName: '',
estimatedReward: 0,
status: '生效中'
status: '1'
}
}
const handleSubmit = () => {
const handleSubmit = async () => {
if (!formData.value.businessCode.trim() || !formData.value.businessName.trim() || formData.value.estimatedReward <= 0) {
ElMessage.error('请填写完整信息')
return
}
if (editingRule.value) {
emit('update', editingRule.value.id, {
businessCode: formData.value.businessCode,
businessName: formData.value.businessName,
estimatedReward: formData.value.estimatedReward,
status: formData.value.status
})
} else {
emit('add', {
businessCode: formData.value.businessCode,
businessName: formData.value.businessName,
estimatedReward: formData.value.estimatedReward,
try {
const response = await $api.updateReward({
id: editingRule.value?editingRule.value.id:'',
jobName: formData.value.businessName,
jobId: formData.value.businessCode,
totalPrice: formData.value.estimatedReward,
status: formData.value.status
})
if(response.c === 0){
ElMessage.success('新建成功')
queryRewardList()
}else{
ElMessage.error(response.m)
}
} catch (error) {
ElMessage.error('文件解析失败,请检查文件格式是否正确')
}
handleCloseDialog()
......@@ -418,20 +371,38 @@ const handleDeleteConfirm = async (id: string) => {
type: 'warning'
}
)
emit('delete', id)
const response = await $api.delReward({
id
})
if(response.c === 0){
ElMessage.success('删除成功')
queryRewardList()
}else{
ElMessage.error(response.m || '删除失败')
}
} catch {
// 用户取消删除
}
}
const handleDownloadTemplate = () => {
const template = '业务名称,业务代码,预计酬金\n示例业务,BEXAMPLE,100'
const blob = new Blob([template], { type: 'text/csv;charset=utf-8;' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = '业务规则批量上传模板.csv'
link.click()
ElMessage.success('模板下载成功')
try {
// 创建下载链接
const link = document.createElement('a')
// 使用相对路径,确保本地和线上都能正常访问
link.href = './static/file/rule_tem.xlsx'
link.download = '业务酬金批量上传模板.xlsx'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
ElMessage.success('模板下载成功')
} catch (error) {
console.error('下载模板失败:', error)
ElMessage.error('模板下载失败,请稍后重试')
}
}
const handleBatchUpload = () => {
......@@ -455,114 +426,69 @@ const handleFileChange = async (e: Event) => {
// 验证文件格式
const fileName = file.name.toLowerCase()
if (!fileName.endsWith('.csv')) {
ElMessage.error('目前仅支持 .csv 格式的文件')
if (!fileName.endsWith('.csv') && !fileName.endsWith('.xlsx')) {
ElMessage.error('目前仅支持 csv和xlsx 格式的文件')
return
}
try {
let data: any[] = []
if (fileName.endsWith('.csv')) {
// 解析 CSV 文件
const text = await file.text()
const lines = text.split('\n').filter(line => line.trim())
if (lines.length < 2) {
ElMessage.error('文件内容为空')
return
const fd = new FormData()
fd.append('file', file)
const response = await $api.uploadReward(fd)
if(response.c === 0){
processUploadData(response.d.list)
}else{
ElMessage.error(response.m)
}
} catch (error) {
ElMessage.error('文件解析失败,请检查文件格式是否正确')
}
}
// 跳过表头,从第二行开始解析
for (let i = 1; i < lines.length; i++) {
const line = (lines[i] || '').trim()
if (!line) continue
const columns = line.split(',').map(col => col.trim())
if (columns.length >= 3) {
data.push({
businessName: columns[0],
businessCode: columns[1],
estimatedReward: columns[2]
})
}
}
} else {
// 解析 XLSX 文件 (暂时禁用)
ElMessage.error('XLSX功能暂未启用,请使用CSV格式')
return
// const arrayBuffer = await file.arrayBuffer()
// const workbook = XLSX.read(arrayBuffer, { type: 'array' })
// const sheetName = workbook.SheetNames[0]
// const worksheet = workbook.Sheets[sheetName]
// const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
// 跳过表头(XLSX 逻辑禁用中)
}
// 验证数据量
if (data.length === 0) {
ElMessage.error('文件中没有有效数据')
return
}
if (data.length > 1000) {
ElMessage.error('数据量超过1000条限制,请分批上传')
return
}
const submitExcelData = async ()=>{
if((uploadResult.value?.failed || 0) > 0){
showUploadResult.value = false
return
}
// 验证并导入数据
processUploadData(data)
try {
const response = await $api.uploadRewardSave({
list: excelShowdData.value
})
if(response.c === 0){
ElMessage.success('上传成功')
showUploadResult.value = false
queryRewardList()
}else{
ElMessage.error(response.m)
}
} catch (error) {
console.error('文件解析失败:', error)
ElMessage.error('文件解析失败,请检查文件格式是否正确')
}
}
const excelShowdData = ref<any[]>([])
const processUploadData = (data: any[]) => {
const errors: string[] = []
let successCount = 0
let failedCount = 0
excelShowdData.value = data
// 获取现有的业务代码集合
const existingCodes = new Set(props.rules.map(r => r.businessCode.toLowerCase()))
const existingCodes = new Set(props.rules.map((r: any) => r.businessCode?.toLowerCase?.() || ''))
data.forEach((row, index) => {
const rowNum = index + 2 // Excel行号(从1开始,跳过表头)
const rowNum = index + 3 // Excel行号(从1开始,跳过表头)
// 验证必填字段
if (!row.businessName || !row.businessCode || !row.estimatedReward) {
errors.push(`第${rowNum}行:缺少必填字段`)
failedCount++
return
}
// 验证业务代码是否重复(与现有数据)
if (existingCodes.has(row.businessCode.toLowerCase())) {
errors.push(`第${rowNum}行:业务代码"${row.businessCode}"已存在,请修改后重新上传`)
failedCount++
return
}
// 验证酬金金额
const amount = parseFloat(row.estimatedReward)
if (isNaN(amount) || amount <= 0) {
errors.push(`第${rowNum}行:预计酬金格式不正确`)
if (row.memo) {
errors.push(`第${rowNum}行:${row.memo}`)
failedCount++
return
}
// 验证通过,添加到系统
try {
emit('add', {
businessCode: row.businessCode,
businessName: row.businessName,
estimatedReward: amount,
status: '生效中'
})
// 将新添加的代码加入集合,避免批量数据内部重复
existingCodes.add(row.businessCode.toLowerCase())
successCount++
} catch (error) {
errors.push(`第${rowNum}行:添加失败`)
......@@ -574,17 +500,9 @@ const processUploadData = (data: any[]) => {
uploadResult.value = {
success: successCount,
failed: failedCount,
errors: errors.slice(0, 20) // 最多显示20条错误
errors: errors
}
showUploadResult.value = true
if (successCount > 0 && failedCount === 0) {
ElMessage.success(`成功导入${successCount}条业务规则`)
} else if (successCount > 0 && failedCount > 0) {
ElMessage.warning(`成功导入${successCount}条,失败${failedCount}条`)
} else {
ElMessage.error(`导入失败,共${failedCount}条错误`)
}
}
</script>
......
......@@ -15,12 +15,11 @@
<span class="text-[16px]">享零工云平台</span>
</h1>
</div>
<!-- 导航菜单 -->
<nav class="flex-1 py-6 px-3 overflow-hidden">
<div class="space-y-6">
<!-- 业务管理菜单 -->
<div class="space-y-1">
<div class="space-y-1" v-if="businessMenuItems.length>0">
<div v-if="!isSidebarCollapsed" class="px-4 mb-2">
<p class="text-xs text-neutral-500 uppercase tracking-wide">业务管理</p>
</div>
......@@ -30,8 +29,7 @@
v-if="isSidebarCollapsed"
:content="item.label"
placement="right"
:delay="0"
>
:delay="0">
<button
@click="handleMenuChange(item.id)"
:class="`
......@@ -57,16 +55,14 @@
? 'bg-brand-primary text-white'
: 'text-neutral-400 hover:bg-white/10 hover:text-neutral-100'
}
`"
>
`">
<component :is="item.icon" class="h-4 w-4 shrink-0" />
<span class="text-[14px]">{{ item.label }}</span>
</button>
</template>
</div>
<!-- 系统管理菜单 -->
<div class="space-y-1">
<div class="space-y-1" v-if="systemMenuItems.length>0">
<div v-if="!isSidebarCollapsed" class="px-4 mb-2">
<p class="text-xs text-neutral-500 uppercase tracking-wide">系统管理</p>
</div>
......@@ -112,7 +108,6 @@
</div>
</div>
</nav>
<!-- 收缩按钮 -->
<div class="p-4 border-t border-neutral-800">
<button
......@@ -134,7 +129,6 @@
</button>
</div>
</div>
<!-- 右侧主内容区 -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- 顶部Header -->
......@@ -190,7 +184,6 @@
</div>
</template>
</div>
<!-- 用户下拉菜单 -->
<el-dropdown trigger="click" @command="handleCommand">
<el-button
......@@ -199,9 +192,9 @@
>
<div class="text-left">
<p class="text-sm text-neutral-900 text-[14px] font-bold">{{ currentUser?.username || '管理员' }}</p>
<p class="text-xs text-neutral-500">
<!-- <p class="text-xs text-neutral-500">
{{ currentUser?.role === 'admin' ? '系统管理员' : '普通用户' }}
</p>
</p> -->
</div>
<ChevronDown :size="16" class="text-neutral-400" />
</el-button>
......@@ -218,7 +211,6 @@
</template>
</el-dropdown>
</header>
<!-- 页面内容区 -->
<main class="flex-1 overflow-auto p-6 bg-[#F0F2F5]">
<!-- 订单监控页面 -->
......@@ -232,8 +224,8 @@
<!-- 订单详情页面 -->
<OrderDetail
v-else-if="activeView === 'order-detail' && selectedOrderId && getOrderById(selectedOrderId)"
:order="getOrderById(selectedOrderId)!"
v-else-if="activeView === 'order-detail' && selectedOrderId"
:order="selectedOrder"
:business-rules="businessRules"
:current-user="currentUser"
@update="handleUpdateOrder"
......@@ -243,9 +235,9 @@
<!-- 业务酬金管理页面 -->
<BusinessRulesManagement
v-else-if="activeView === 'business'"
:rules="businessRules"
@add="handleAddBusinessRule"
@update="handleUpdateBusinessRule"
:rules="businessRules as any"
@add="handleAddBusinessRule as any"
@update="handleUpdateBusinessRule as any"
@toggle-status="handleToggleBusinessRuleStatus"
@delete="handleDeleteBusinessRule"
/>
......@@ -253,7 +245,7 @@
<!-- 角色管理页面 -->
<RoleManagement
v-else-if="activeView === 'roles'"
:roles="roles"
:roles="roles as any"
:permissions="permissions"
@add-role="handleAddRole"
@update-role="handleUpdateRole"
......@@ -278,9 +270,8 @@
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed ,onMounted,getCurrentInstance} from 'vue'
import {
LayoutDashboard,
DollarSign,
......@@ -292,66 +283,115 @@ import {
ChevronDown
} from 'lucide-vue-next'
import { ElMessage, ElMessageBox } from 'element-plus'
// 导入图片资源
import platformLogoImg from '@/assets/8a3322d5ba8c2ae3592af24d73566a63828a3a27.png'
// 导入子组件
import OrderMonitoring from './OrderMonitoring.vue'
import OrderDetail from './OrderDetail.vue'
import BusinessRulesManagement from './BusinessRulesManagement.vue'
import RoleManagement from './RoleManagement.vue'
import UserManagement from './UserManagement.vue'
import RoleManagement, { type Permission } from './RoleManagement.vue'
import UserManagement, { type User as UserType } from './UserManagement.vue'
import RoleIcon from './icons/RoleIcon.vue'
const platformLogo = ref(platformLogoImg)
// 获取全局API实例
const { $api } = getCurrentInstance()!.appContext.config.globalProperties
// 本地Role类型定义,确保permissions字段是必需的
interface Role {
id: string
roleName: string
name: string
level: '地市级' | '区县级' | '一线人员'
status: '启用' | '禁用'
remark?: string
permissionIds?: string[]
permissions: string[]
createTime?: string
}
interface DesktopMainProps {
currentUser: { username: string; role: 'admin' | 'viewer' } | null;
}
const props = defineProps<DesktopMainProps>()
const emit = defineEmits(['logout'])
// 响应式数据
const isSidebarCollapsed = ref(false)
const activeMenu = ref<string>('orders') // 当前激活的菜单
const activeView = ref<string>('orders') // 当前显示的视图
const selectedOrderId = ref<string | null>(null) // 当前查看的订单ID
const selectedOrder = ref<any>(null) // 当前查看的订单
const userInfo = ref(localStorage.getItem('pcUserInfo') ? JSON.parse(localStorage.getItem('pcUserInfo') as string) : {})
// 菜单配置
const businessMenuItems = [
{ id: 'orders', label: '登记订单管理', icon: LayoutDashboard },
{ id: 'business', label: '业务酬金管理', icon: DollarSign }
]
// const businessMenuItems = [
// { id: 'orders', label: '登记订单管理', icon: LayoutDashboard },
// { id: 'business', label: '业务酬金管理', icon: DollarSign }
// ]
const businessMenuItems = computed(() => {
let va = userInfo.value
if(va?.functions){
let arr = va.functions.filter((item: any) => {return item.id==1})
if(arr.length>0 && arr[0].children){
let barr: any[] = []
arr[0].children.forEach((item: any) => {
if(item.id == 2){
barr.push({ id: 'orders', label: '登记订单管理', icon: LayoutDashboard })
}
if(item.id == 8){
barr.push({ id: 'business', label: '业务酬金管理', icon: DollarSign })
}
})
const systemMenuItems = [
{ id: 'roles', label: '角色管理', icon: RoleIcon },
{ id: 'users', label: '账号管理', icon: Users }
]
return barr
}
}
return []
})
// const systemMenuItems = [
// { id: 'roles', label: '角色管理', icon: RoleIcon },
// { id: 'users', label: '账号管理', icon: Users }
// ]
const systemMenuItems = computed(() => {
let va = userInfo.value
if(va?.functions){
let arr = va.functions.filter((item: any) => {return item.id==14})
if(arr.length>0 && arr[0].children){
let barr: any[] = []
arr[0].children.forEach((item: any) => {
if(item.id == 15){
barr.push({ id: 'roles', label: '角色管理', icon: RoleIcon })
}
if(item.id == 19){
barr.push({ id: 'users', label: '账号管理', icon: Users })
}
})
return barr
}
}
return []
})
const menuMap: Record<string, string> = {
orders: '登记订单管理',
business: '业务酬金管理',
roles: '角色管理',
users: '账号管理',
}
const currentMenuTitle = computed(() => {
if (activeView.value === 'order-detail') {
return '订单详情'
}
return menuMap[activeView.value] || '首页'
})
// 业务规则数据
const businessRules = ref([
const businessRules = ref<BusinessRule[]>([
{
id: 'BIZ001',
businessCode: 'B5G001',
businessName: '5G套餐办理',
estimatedReward: 50,
status: '生效中',
jobName: '5G套餐办理',
jobId: 'JOB001',
preMoney: 50,
totalPrice: 50,
createTime: '2025-10-20 10:00'
},
{
......@@ -360,6 +400,10 @@ const businessRules = ref([
businessName: '宽带新装',
estimatedReward: 80,
status: '生效中',
jobName: '宽带新装',
jobId: 'JOB002',
preMoney: 80,
totalPrice: 80,
createTime: '2025-10-20 10:05'
},
{
......@@ -368,6 +412,10 @@ const businessRules = ref([
businessName: '话费充值',
estimatedReward: 30,
status: '生效中',
jobName: '话费充值',
jobId: 'JOB003',
preMoney: 30,
totalPrice: 30,
createTime: '2025-10-20 10:10'
},
{
......@@ -376,235 +424,52 @@ const businessRules = ref([
businessName: '流量包办理',
estimatedReward: 40,
status: '已停用',
jobName: '流量包办理',
jobId: 'JOB004',
preMoney: 40,
totalPrice: 40,
createTime: '2025-10-20 10:15'
}
])
// 模拟订单数据
// 使用空数组,让 OrderMonitoring 组件自动生成模拟数据
const orders = ref([])
// 存储从 OrderMonitoring 组件获取的订单数据
const allOrders = ref([])
const allOrders = ref([] as any[])
// 类型定义
interface Role {
interface BusinessRule {
id: string
name: string
description?: string
level: '地市级' | '区县级' | '一线人员'
permissionIds: string[]
status: '启用' | '禁用'
createTime?: string
businessCode: string
businessName: string
estimatedReward: number
status: "生效中" | "已停用"
createTime: string
jobName: string
preMoney: number
jobId: string
totalPrice: number
}
interface Permission {
interface Organization {
id: string
name: string
code: string
description?: string
areaCode: string
type: "地市" | "区县" | "客户经理团队"
parentId?: string
children?: Permission[]
children?: Organization[]
}
// 角色管理数据
const roles = ref<Role[]>([
{
id: 'role-001',
name: '区县全权管理员',
description: '拥有区县级别的所有管理权限,可以管理订单、用户和业务规则',
level: '区县级',
permissionIds: [
'order',
'order:view',
'order:complete',
'order:reward',
'order:approve',
'business',
'business:view',
'business:create',
'business:edit',
'system',
'system:role',
'system:role:view',
'system:role:create',
'system:role:edit',
'system:account',
'system:account:view',
'system:account:create',
'system:account:edit'
],
status: '启用',
createTime: '2025-10-20 09:00:00'
},
{
id: 'role-002',
name: '订单管理员',
description: '负责订单的日常管理和处理',
level: '一线人员',
permissionIds: ['order', 'order:view', 'order:complete', 'order:reward', 'order:approve'],
status: '启用',
createTime: '2025-10-20 09:30:00'
},
{
id: 'role-003',
name: '业务酬金管理员',
description: '负责业务酬金的配置和维护',
level: '区县级',
permissionIds: ['business', 'business:view', 'business:create', 'business:edit'],
status: '启用',
createTime: '2025-10-20 10:00:00'
}
])
const roles = ref<Role[]>([])
// 权限数据
const permissions = ref<Permission[]>([
{
id: 'order',
name: '订单管理',
code: 'order',
description: '订单相关的所有权限',
children: [
{
id: 'order:view',
name: '查看订单',
code: 'order:view',
description: '查看订单列表和详情',
parentId: 'order'
},
{
id: 'order:complete',
name: '填写办结信息',
code: 'order:complete',
description: '填写CRM订单编号和办理备注',
parentId: 'order'
},
{
id: 'order:reward',
name: '填写酬金金额',
code: 'order:reward',
description: '填写和修改实际发放酬金',
parentId: 'order'
},
{
id: 'order:approve',
name: '审核',
code: 'order:approve',
description: '审核通过或驳回订单',
parentId: 'order'
}
]
},
{
id: 'business',
name: '业务酬金管理',
code: 'business',
description: '业务酬金相关权限',
children: [
{
id: 'business:view',
name: '查看业务酬金',
code: 'business:view',
description: '查看业务酬金列表',
parentId: 'business'
},
{
id: 'business:create',
name: '创建业务酬金',
code: 'business:create',
description: '创建新的业务酬金',
parentId: 'business'
},
{
id: 'business:edit',
name: '编辑业务酬金',
code: 'business:edit',
description: '修改和停用业务酬金',
parentId: 'business'
}
]
},
{
id: 'system',
name: '系统管理',
code: 'system',
description: '系统管理相关权限',
children: [
{
id: 'system:role',
name: '角色管理',
code: 'system:role',
description: '角色管理相关权限',
parentId: 'system',
children: [
{
id: 'system:role:view',
name: '查看角色',
code: 'system:role:view',
description: '查看角色列表',
parentId: 'system:role'
},
{
id: 'system:role:create',
name: '创建角色',
code: 'system:role:create',
description: '创建新角色',
parentId: 'system:role'
},
{
id: 'system:role:edit',
name: '编辑角色',
code: 'system:role:edit',
description: '编辑角色信息和权限',
parentId: 'system:role'
}
]
},
{
id: 'system:account',
name: '账号管理',
code: 'system:account',
description: '账号管理相关权限',
parentId: 'system',
children: [
{
id: 'system:account:view',
name: '查看账号',
code: 'system:account:view',
description: '查看账号列表',
parentId: 'system:account'
},
{
id: 'system:account:create',
name: '创建账号',
code: 'system:account:create',
description: '创建新账号',
parentId: 'system:account'
},
{
id: 'system:account:edit',
name: '编辑账号',
code: 'system:account:edit',
description: '编辑账号信息',
parentId: 'system:account'
}
]
}
]
}
])
const permissions = ref<Permission[]>(userInfo.value?.functions || [])
// 根据订单ID获取订单详情
const getOrderById = (orderId: string) => {
return allOrders.value.find(order => order.id === orderId)
return allOrders.value.find((order: any) => order.id === orderId)
}
// 方法
const handleMenuChange = (menuId: string) => {
activeMenu.value = menuId
activeView.value = menuId
}
const handleCommand = (command: string) => {
if (command === 'logout') {
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
......@@ -621,32 +486,30 @@ const handleCommand = (command: string) => {
ElMessage.info(`点击了 ${command}`)
}
}
const handleViewOrderDetail = (order: any) => {
selectedOrder.value = order
selectedOrderId.value = order.id
activeView.value = 'order-detail'
}
const handleBackToOrders = () => {
selectedOrderId.value = null
activeView.value = 'orders'
}
const handleUpdateOrder = (updates: any) => {
// 更新 allOrders 中的订单数据
const orderIndex = allOrders.value.findIndex(order => order.id === selectedOrderId.value)
if (orderIndex !== -1) {
allOrders.value[orderIndex] = { ...allOrders.value[orderIndex], ...updates }
if (selectedOrderId.value) {
const orderIndex = allOrders.value.findIndex(order => order.id === selectedOrderId.value)
if (orderIndex !== -1) {
allOrders.value[orderIndex] = { ...allOrders.value[orderIndex], ...updates }
}
}
console.log('更新订单:', updates)
ElMessage.success('订单更新成功')
}
// 接收来自 OrderMonitoring 组件的订单数据
const handleOrdersUpdate = (ordersList: any[]) => {
allOrders.value = ordersList
}
// 面包屑导航点击处理
const handleBreadcrumbClick = (level: string) => {
if (level === 'level1') {
......@@ -660,9 +523,8 @@ const handleBreadcrumbClick = (level: string) => {
}
}
}
// 业务规则管理方法
const handleAddBusinessRule = (ruleData: Omit<any, 'id' | 'createTime'>) => {
const handleAddBusinessRule = (ruleData: Omit<BusinessRule, 'id' | 'createTime'>) => {
const newRule = {
id: `BIZ${String(businessRules.value.length + 1).padStart(3, '0')}`,
...ruleData,
......@@ -678,44 +540,34 @@ const handleAddBusinessRule = (ruleData: Omit<any, 'id' | 'createTime'>) => {
ElMessage.success('业务规则创建成功')
console.log('新增业务规则:', newRule)
}
const handleUpdateBusinessRule = (ruleId: string, updates: any) => {
const handleUpdateBusinessRule = (ruleId: string, updates: Partial<BusinessRule>) => {
const ruleIndex = businessRules.value.findIndex(rule => rule.id === ruleId)
if (ruleIndex !== -1) {
businessRules.value[ruleIndex] = {
...businessRules.value[ruleIndex],
...updates,
updateTime: new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
const existingRule = businessRules.value[ruleIndex]
businessRules.value[ruleIndex] = {
...existingRule,
...updates
} as BusinessRule
ElMessage.success('业务规则更新成功')
console.log('更新业务规则:', ruleId, updates)
}
}
const handleToggleBusinessRuleStatus = (ruleId: string) => {
const ruleIndex = businessRules.value.findIndex(rule => rule.id === ruleId)
if (ruleIndex !== -1) {
const currentStatus = businessRules.value[ruleIndex].status
const newStatus = currentStatus === '生效中' ? '已停用' : '生效中'
businessRules.value[ruleIndex].status = newStatus
businessRules.value[ruleIndex].updateTime = new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
ElMessage.success(`业务规则已${newStatus === '生效中' ? '启用' : '停用'}`)
console.log('切换业务规则状态:', ruleId, newStatus)
const rule = businessRules.value[ruleIndex]
if (rule) {
const currentStatus = rule.status
const newStatus = currentStatus === '生效中' ? '已停用' : '生效中'
businessRules.value[ruleIndex] = {
...rule,
status: newStatus
}
ElMessage.success(`业务规则已${newStatus === '生效中' ? '启用' : '停用'}`)
console.log('切换业务规则状态:', ruleId, newStatus)
}
}
}
const handleDeleteBusinessRule = (ruleId: string) => {
const ruleIndex = businessRules.value.findIndex(rule => rule.id === ruleId)
if (ruleIndex !== -1) {
......@@ -724,12 +576,15 @@ const handleDeleteBusinessRule = (ruleId: string) => {
console.log('删除业务规则:', ruleId)
}
}
// 角色管理方法
const handleAddRole = (roleData: Omit<Role, 'id' | 'createTime'>) => {
const newRole = {
id: `role-${Date.now()}`,
...roleData,
const handleAddRole = (roleData: any) => {
const newRole: Role = {
id: String(Date.now()),
roleName: roleData.roleName || roleData.name || '',
name: roleData.name || roleData.roleName,
level: roleData.level,
permissions: roleData.permissions || roleData.permissionIds || [],
status: '启用',
createTime: new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
......@@ -742,73 +597,60 @@ const handleAddRole = (roleData: Omit<Role, 'id' | 'createTime'>) => {
roles.value.push(newRole)
console.log('新增角色:', newRole)
}
const handleUpdateRole = (roleId: string, updates: Partial<Role>) => {
const roleIndex = roles.value.findIndex(role => role.id === roleId)
const handleUpdateRole = (roleId: string, updates: any) => {
const roleIndex = roles.value.findIndex((role: any) => String(role.id) === roleId)
if (roleIndex !== -1) {
roles.value[roleIndex] = { ...roles.value[roleIndex], ...updates }
console.log('更新角色:', roleId, updates)
const existingRole = roles.value[roleIndex]
if (existingRole) {
roles.value[roleIndex] = {
...existingRole,
...updates,
permissions: updates.permissions || updates.permissionIds || existingRole.permissions
} as Role
console.log('更新角色:', roleId, updates)
}
}
}
// 组织架构数据
const organizations = ref([
{
id: 'org-001',
name: '杭州市移动',
type: '地市',
children: [
{
id: 'org-002',
name: '西湖区',
type: '区县',
parentId: 'org-001',
children: [
{
id: 'org-005',
name: '客户经理团队',
type: '客户经理团队',
parentId: 'org-002'
}
]
},
{
id: 'org-003',
name: '滨江区',
type: '区县',
parentId: 'org-001',
children: [
{
id: 'org-006',
name: '客户经理团队',
type: '客户经理团队',
parentId: 'org-003'
}
]
},
{
id: 'org-004',
name: '余杭区',
type: '区县',
parentId: 'org-001',
children: [
{
id: 'org-007',
name: '客户经理团队',
type: '客户经理团队',
parentId: 'org-004'
}
]
const organizations = ref<Organization[]>([])
// 生命周期
onMounted(() => {
getArea()
})
const getArea = async ()=>{
try {
const response = await $api.queryAreaData({
areaId: userInfo.value.areaId
})
if (response && response.c === 0) {
let pa = response.d
pa.id += ''
pa.type = pa.areaLevel==2?'地市':'区县'
pa.name = pa.areaName
pa.parentId += ''
pa.children.forEach((item: any) => {
item.id += ''
item.type = item.areaLevel==2?'地市':'区县'
item.name = item.areaName
item.parentId += ''
})
organizations.value = [pa]
console.log(pa)
}
]
}
])
} catch (error) {
}
}
// 当前登录用户ID
const currentUserId = ref('user-001')
// 用户数据
const users = ref([
const users = ref<UserType[]>([
{
id: 'user-001',
username: 'admin',
......@@ -817,7 +659,7 @@ const users = ref([
roleId: 'role-001',
phone: '13800138000',
email: 'admin@cmcc.com',
status: '正常',
status: '1' as '0' | '1',
createTime: '2025-10-20 08:00:00',
creatorId: 'system'
},
......@@ -829,7 +671,7 @@ const users = ref([
roleId: 'role-002',
phone: '13800138001',
email: 'zhangsan@cmcc.com',
status: '正常',
status: '1' as '0' | '1',
createTime: '2025-10-20 09:00:00',
creatorId: 'user-001'
},
......@@ -841,7 +683,7 @@ const users = ref([
roleId: 'role-002',
phone: '13800138002',
email: 'lisi@cmcc.com',
status: '禁用',
status: '0' as '0' | '1',
createTime: '2025-10-20 10:00:00',
creatorId: 'user-001'
},
......@@ -853,16 +695,16 @@ const users = ref([
roleId: 'role-003',
phone: '13800138003',
email: 'wangwu@cmcc.com',
status: '正常',
status: '1' as '0' | '1',
createTime: '2025-10-20 11:00:00',
creatorId: 'user-001'
}
])
// 用户管理方法
const handleAddUser = (userData: any) => {
const handleAddUser = (userData: Omit<UserType, 'id' | 'createTime' | 'creatorId'>) => {
const newUser = {
id: `user-${Date.now()}`,
creatorId: props.currentUser?.username || 'system',
...userData,
createTime: new Date().toLocaleString('zh-CN', {
year: 'numeric',
......@@ -872,19 +714,21 @@ const handleAddUser = (userData: any) => {
minute: '2-digit',
second: '2-digit'
})
}
} as UserType
users.value.push(newUser)
console.log('新增用户:', newUser)
}
const handleUpdateUser = (userId: string, updates: any) => {
const handleUpdateUser = (userId: string, updates: Partial<UserType>) => {
const userIndex = users.value.findIndex(user => user.id === userId)
if (userIndex !== -1) {
users.value[userIndex] = { ...users.value[userIndex], ...updates }
const existingUser = users.value[userIndex]
users.value[userIndex] = {
...existingUser,
...updates
} as UserType
console.log('更新用户:', userId, updates)
}
}
const handleDeleteUser = (userId: string) => {
const userIndex = users.value.findIndex(user => user.id === userId)
if (userIndex !== -1) {
......@@ -893,18 +737,15 @@ const handleDeleteUser = (userId: string) => {
}
}
</script>
<style scoped>
/* 隐藏滚动条但保持滚动功能 */
.hide-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
.hide-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, and Opera */
}
/* 移除右上角用户信息按钮的悬浮背景效果 */
:deep(header .el-button.is-text:hover),
:deep(header .el-button.is-text:focus),
......
......@@ -4,24 +4,24 @@
<div class="absolute inset-0">
<div class="absolute inset-0 bg-[#001529]" />
<div class="absolute inset-0 opacity-50 overflow-hidden">
<img
alt=""
class="absolute h-full left-0 max-w-none top-0 w-auto min-w-full object-cover"
:src="loginBackground"
<img
alt=""
class="absolute h-full left-0 max-w-none top-0 w-auto min-w-full object-cover"
:src="loginBackground"
/>
</div>
</div>
<!-- 内容区域 -->
<div class="relative z-10 w-full flex flex-col items-center justify-center gap-8 px-6">
<!-- Logo和标题区 -->
<div class="flex flex-col items-center gap-2 w-full max-w-[448px]">
<div class="relative w-[88px] h-[88px]">
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<img
alt="享零工云平台"
class="absolute h-[85.72%] left-[-8.06%] max-w-none top-[7.7%] w-[329.57%]"
:src="logoIcon"
<img
alt="享零工云平台"
class="absolute h-[85.72%] left-[-8.06%] max-w-none top-[7.7%] w-[329.57%]"
:src="logoIcon"
/>
</div>
</div>
......@@ -30,104 +30,108 @@
</div>
<!-- 登录卡片 -->
<div
<div
class="relative w-full max-w-[448px] backdrop-blur-[10px] backdrop-filter rounded-[10px] pt-10 pb-10 px-8 border border-[rgba(255,255,255,0.6)] shadow-[0_25px_50px_-12px_rgba(0,0,0,0.25)]"
:style="{
background: 'linear-gradient(142deg, rgba(255, 255, 255, 0.32) 6%, rgba(255, 255, 255, 0.20) 90.9%)'
}"
>
<el-form
<el-form
ref="loginFormRef"
:model="loginForm"
:model="loginForm"
:rules="loginRules"
@submit.prevent="handleSubmit"
class="relative flex flex-col gap-7"
>
<!-- 手机号 -->
<div class="flex flex-col gap-2.5">
<label for="phone" class="text-white">
<label class="text-white">
手机号
</label>
<el-input
id="phone"
v-model="loginForm.phone"
type="tel"
placeholder="请输入手机号"
maxlength="11"
class="login-input"
:disabled="isLoading"
size="large"
/>
<el-form-item prop="phone" class="mb-0">
<el-input
v-model="loginForm.phone"
type="tel"
placeholder="请输入手机号"
maxlength="11"
class="login-input"
:disabled="isLoading"
size="large"
/>
</el-form-item>
</div>
<!-- 图形验证码 -->
<div class="flex flex-col gap-2.5">
<label for="captcha" class="text-white">
<label class="text-white">
图形验证码
</label>
<div class="flex gap-2">
<el-input
id="captcha"
v-model="loginForm.captchaInput"
type="text"
placeholder="请输入图形验证码"
maxlength="4"
class="login-input flex-1"
:disabled="isLoading"
size="large"
/>
<div
class="relative h-12 w-[120px] bg-white rounded-lg overflow-hidden cursor-pointer hover:opacity-80 transition-opacity"
@click="generateCaptcha"
title="点击刷新验证码"
>
<img
v-if="captchaImage"
:src="captchaImage"
alt="验证码"
class="w-full h-full object-cover"
<el-form-item prop="captchaInput" class="mb-0">
<div class="flex gap-2" style="width: 100%;">
<el-input
v-model="loginForm.captchaInput"
type="text"
placeholder="请输入图形验证码"
maxlength="4"
class="login-input flex-1"
:disabled="isLoading"
size="large"
/>
<RefreshCw class="absolute top-1 right-1 w-3 h-3 text-gray-400" />
<div
class="relative h-12 w-[120px] bg-white rounded-lg overflow-hidden cursor-pointer hover:opacity-80 transition-opacity"
@click="generateCaptcha"
title="点击刷新验证码"
>
<img
v-if="captchaImage"
:src="captchaImage"
alt="验证码"
class="w-full h-full object-cover"
/>
</div>
</div>
</div>
</el-form-item>
</div>
<!-- 短信验证码 -->
<div class="flex flex-col gap-2.5">
<label for="smsCode" class="text-white">
<label class="text-white">
短信验证码
</label>
<div class="flex gap-2">
<el-input
id="smsCode"
v-model="loginForm.smsCode"
type="text"
placeholder="请输入短信验证码"
maxlength="6"
class="login-input flex-1"
:disabled="isLoading"
size="large"
/>
<el-button
@click="handleSendSms"
:disabled="!canSendSms || isLoading"
class="sms-button-white"
size="large"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
</el-button>
</div>
<el-form-item prop="smsCode" class="mb-0">
<div class="flex gap-2" style="width: 100%;">
<el-input
v-model="loginForm.smsCode"
type="text"
placeholder="请输入短信验证码"
maxlength="6"
class="login-input flex-1"
:disabled="isLoading"
size="large"
/>
<el-button
@click="handleSendSms"
:disabled="!canSendSms || isLoading"
class="sms-button-white"
size="large"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '获取验证码' }}
</el-button>
</div>
</el-form-item>
</div>
<el-button
type="primary"
@click="handleSubmit"
class="login-submit-button"
:loading="isLoading"
size="large"
>
{{ isLoading ? '登录中...' : '登录' }}
</el-button>
<div class="mt-2">
<el-button
type="primary"
@click="handleSubmit"
class="login-submit-button"
:loading="isLoading"
size="large"
>
{{ isLoading ? '登录中...' : '登录' }}
</el-button>
</div>
</el-form>
</div>
</div>
......@@ -144,7 +148,7 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive } from 'vue'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { RefreshCw } from 'lucide-vue-next'
import { getCurrentInstance } from 'vue'
// 导入图片资源
import loginBackgroundImg from '@/assets/7f0599d246217c734650d105801453a4919de13c.png'
......@@ -167,19 +171,22 @@ interface LoginProps {
// Props
const props = defineProps<LoginProps>()
// 获取全局API实例
const { $api } = getCurrentInstance()!.appContext.config.globalProperties
// 响应式数据
const loginFormRef = ref<FormInstance>()
const isLoading = ref(false)
const captchaText = ref('')
const captchaToken = ref('')
const captchaImage = ref('')
const countdown = ref(0)
const canSendSms = ref(true)
// 表单数据
const loginForm = reactive<LoginForm>({
phone: '13800000001',
phone: '13112345678',
captchaInput: '',
smsCode: ''
smsCode: '123456'
})
// 表单验证规则
......@@ -198,61 +205,29 @@ const loginRules: FormRules<LoginForm> = {
]
}
// 生成图形验证码 - 完全复制React版本的逻辑
const generateCaptcha = () => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let text = ''
for (let i = 0; i < 4; i++) {
text += chars.charAt(Math.floor(Math.random() * chars.length))
}
captchaText.value = text
// 生成验证码图片 (使用 canvas)
const canvas = document.createElement('canvas')
canvas.width = 120
canvas.height = 40
const ctx = canvas.getContext('2d')
if (ctx) {
// 背景
ctx.fillStyle = '#f0f0f0'
ctx.fillRect(0, 0, canvas.width, canvas.height)
// 干扰线
for (let i = 0; i < 5; i++) {
ctx.strokeStyle = `rgba(${Math.random() * 255},${Math.random() * 255},${Math.random() * 255},0.3)`
ctx.beginPath()
ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height)
ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height)
ctx.stroke()
}
// 验证码文字
ctx.font = 'bold 24px Arial'
ctx.textBaseline = 'middle'
for (let i = 0; i < text.length; i++) {
ctx.fillStyle = `rgb(${Math.random() * 100},${Math.random() * 100},${Math.random() * 100})`
const x = 20 + i * 25
const y = 20 + (Math.random() - 0.5) * 8
const angle = (Math.random() - 0.5) * 0.4
ctx.save()
ctx.translate(x, y)
ctx.rotate(angle)
ctx.fillText(text[i], 0, 0)
ctx.restore()
// 从接口获取图形验证码
const generateCaptcha = async () => {
try {
const response = await $api.getImgCode({})
if (response && response.c === 0) {
captchaImage.value = 'data:image/png;base64,'+response.d.image
// 保存验证码标识,用于后续验证
captchaToken.value = response.d.imageId
} else {
ElMessage.error('获取图形验证码失败')
}
captchaImage.value = canvas.toDataURL()
} catch (error) {
ElMessage.error('获取图形验证码失败')
}
}
// 倒计时逻辑
let countdownTimer: NodeJS.Timeout | null = null
let countdownTimer: number | null = null
const startCountdown = () => {
countdown.value = 60
canSendSms.value = false
countdownTimer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
......@@ -266,53 +241,74 @@ const startCountdown = () => {
}
// 发送短信验证码
const handleSendSms = () => {
const handleSendSms = async () => {
// 验证手机号
if (!loginForm.phone.trim()) {
ElMessage.error('请输入手机号')
return
}
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(loginForm.phone)) {
ElMessage.error('请输入正确的手机号')
return
}
// 验证图形验证码
if (!loginForm.captchaInput.trim()) {
ElMessage.error('请输入图形验证码')
return
}
if (loginForm.captchaInput.toUpperCase() !== captchaText.value) {
ElMessage.error('图形验证码错误')
// 模拟验证图形验证码(实际应该调用接口验证)
if (!captchaToken.value) {
ElMessage.error('请先获取图形验证码')
return
}
try {
// 调用获取短信验证码接口
const response = await $api.getTelCode({
phone: loginForm.phone,
code: loginForm.captchaInput,
imageId: captchaToken.value
})
if (response && response.c === 0) {
ElMessage.success('验证码已发送至您的手机,请注意查收')
startCountdown()
// 演示用:实际验证码为 123456
console.log('演示验证码:123456')
} else {
ElMessage.error(response?.m || '发送验证码失败')
generateCaptcha()
loginForm.captchaInput = ''
}
} catch (error) {
console.error('发送短信验证码失败:', error)
ElMessage.error('发送验证码失败')
generateCaptcha()
loginForm.captchaInput = ''
return
}
// 模拟发送短信
ElMessage.success('验证码已发送至您的手机,请注意查收')
startCountdown()
// 演示用:实际验证码为 123456
console.log('演示验证码:123456')
}
// 登录提交
const handleSubmit = async () => {
if (!loginFormRef.value) return
try {
await loginFormRef.value.validate()
} catch {
// 表单校验
const valid = await loginFormRef.value.validate()
console.log('表单验证结果:', valid)
if (!valid) {
console.log('表单验证失败')
ElMessage.error('请检查表单填写是否正确')
return
}
// 验证图形验证码
if (loginForm.captchaInput.toUpperCase() !== captchaText.value) {
ElMessage.error('图形验证码错误')
// 验证图形验证码(实际应该调用接口验证)
if (!captchaToken.value) {
ElMessage.error('请先获取图形验证码')
generateCaptcha()
loginForm.captchaInput = ''
return
......@@ -320,24 +316,37 @@ const handleSubmit = async () => {
isLoading.value = true
// 模拟登录验证 - 完全复制React版本的逻辑
setTimeout(() => {
// 演示账号:
// 13800000001 验证码123456 - 管理员
// 13800000002 验证码123456 - 普通用户
if (loginForm.phone === '13800000001' && loginForm.smsCode === '123456') {
ElMessage.success('登录成功')
props.onLogin(loginForm.phone, 'admin')
} else if (loginForm.phone === '13800000002' && loginForm.smsCode === '123456') {
try {
// 调用手机登录接口
const response = await $api.pohoneLogin({
phone: loginForm.phone,
code: loginForm.smsCode,
// captcha: loginForm.captchaInput,
// captchaToken: captchaToken.value
})
if (response && response.c === 0) {
ElMessage.success('登录成功')
props.onLogin(loginForm.phone, 'viewer')
// 保存登录信息
if (response.d) {
localStorage.setItem('pcUserInfo', JSON.stringify(response.d))
}
props.onLogin(response.d.nickname, 'admin')
} else {
ElMessage.error('手机号或验证码错误')
isLoading.value = false
ElMessage.error(response?.m || '登录失败')
generateCaptcha()
loginForm.captchaInput = ''
loginForm.smsCode = ''
}
}, 800)
} catch (error) {
console.error('登录失败:', error)
ElMessage.error('登录失败')
generateCaptcha()
loginForm.captchaInput = ''
loginForm.smsCode = ''
} finally {
isLoading.value = false
}
}
// 生命周期
......@@ -440,4 +449,9 @@ label {
font-weight: 500;
line-height: 1.5;
}
</style>
/* 重置 el-form-item 的默认边距 */
:deep(.el-form-item) {
margin-bottom: 0;
}
</style>
\ No newline at end of file
......@@ -13,66 +13,17 @@
<h1 class="text-2xl font-bold text-neutral-900 m-0" style="margin-left: 8px;">订单详情</h1>
</div>
<div class="flex items-center gap-2">
<!-- 待办理:关闭订单 + 提交审核 -->
<template v-if="order.businessStatus === '待办理'">
<el-button
@click="isCloseDialogOpen = true"
class="border-neutral-500 text-neutral-700"
>
关闭订单
</el-button>
<el-button
type="primary"
@click="handleSaveCompleteInfo"
class="bg-brand-primary"
>
提交审核
</el-button>
</template>
<!-- 办理成功:关闭订单 + 提交审核 -->
<template v-else-if="order.businessStatus === '办理成功' && !order.reviewStatus">
<el-button
@click="isCloseDialogOpen = true"
class="border-neutral-500 text-neutral-700"
>
关闭订单
</el-button>
<el-button
type="primary"
@click="handleSubmitReview"
class="bg-brand-primary"
>
提交审核
</el-button>
</template>
<!-- 办理成功+审核驳回:关闭订单 + 重新提交审核 -->
<template v-else-if="order.businessStatus === '办理成功' && order.reviewStatus === '审核驳回'">
<el-button
@click="isCloseDialogOpen = true"
class="border-neutral-500 text-neutral-700"
>
关闭订单
</el-button>
<el-button
type="primary"
@click="handleSubmitReview"
class="bg-brand-primary"
>
重新提交审核
</el-button>
</template>
<div class="flex items-center gap-2" v-if="order.status !== '0'">
<el-button
@click="isCloseDialogOpen = true"
v-if="order.auditStatus !== '1'"
class="border-neutral-500 text-neutral-700"
>
关闭订单
</el-button>
<!-- 待审核:关闭订单 + 驳回 + 通过 -->
<template v-else-if="order.businessStatus === '办理成功' && order.reviewStatus === '待审核' && currentUser?.role === 'admin'">
<el-button
@click="isCloseDialogOpen = true"
class="border-neutral-500 text-neutral-700"
>
关闭订单
</el-button>
<template v-if="order.auditStatus === '0'">
<el-dropdown @command="handleAuditCommand">
<el-button type="primary" class="bg-brand-primary">
审核订单
......@@ -93,15 +44,6 @@
</el-dropdown>
</template>
<!-- 审核通过:仅显示关闭订单;业务状态为已关闭时不显示任何操作按钮 -->
<template v-else-if="order.businessStatus !== '已关闭'">
<el-button
@click="isCloseDialogOpen = true"
class="border-neutral-500 text-neutral-700"
>
关闭订单
</el-button>
</template>
</div>
</div>
......@@ -119,15 +61,15 @@
</div>
<div>
<label class="text-neutral-500 text-sm">能人</label>
<p class="mt-2 text-neutral-900">{{ order.registerPersonName }}</p>
<p class="mt-2 text-neutral-900">{{ order.chinaPersonName }}</p>
</div>
<div>
<label class="text-neutral-500 text-sm">能人手机号</label>
<p class="mt-2 text-neutral-900">{{ order.registerPersonPhone }}</p>
<p class="mt-2 text-neutral-900">{{ order.chinaPersonPhone }}</p>
</div>
<div>
<label class="text-neutral-500 text-sm">登记时间</label>
<p class="mt-2 text-neutral-700">{{ order.registerTime }}</p>
<p class="mt-2 text-neutral-700">{{ $utils.detailTime(order.createTime) }}</p>
</div>
<div>
<label class="text-neutral-500 text-sm">客户号码</label>
......@@ -135,67 +77,67 @@
</div>
<div>
<label class="text-neutral-500 text-sm">业务名称</label>
<p class="mt-2 text-neutral-900">{{ order.businessName }}</p>
<p class="mt-2 text-neutral-900">{{ order.jobName }}</p>
</div>
<div>
<label class="text-neutral-500 text-sm">业务状态</label>
<div class="mt-2">
<span
:class="getBusinessStatusClass(order.businessStatus)"
:class="getstatusClass(order.status)"
class="px-2 py-1 rounded text-xs font-medium"
>
{{ order.businessStatus }}
{{ getstatusName(order.status) }}
</span>
</div>
</div>
<div v-if="order.reviewStatus">
<div v-if="order.auditStatus">
<label class="text-neutral-500 text-sm">审核状态</label>
<div class="mt-2">
<span
:class="getReviewStatusClass(order.reviewStatus)"
:class="getauditStatusClass(order.auditStatus)"
class="px-2 py-1 rounded text-xs font-medium"
>
{{ order.reviewStatus }}
{{ getauditStatusName(order.auditStatus) }}
</span>
</div>
</div>
<div v-if="order.remarks" class="col-span-2">
<label class="text-neutral-500 text-sm">备注信息</label>
<p class="mt-2 text-neutral-700">{{ order.remarks }}</p>
<p class="mt-2 text-neutral-700">{{ order.memo||'--' }}</p>
</div>
</div>
</el-card>
<!-- 操作日志 -->
<el-card v-if="order.operationLogs && order.operationLogs.length > 0" class="p-6">
<el-card v-if="logData.length > 0" class="p-6">
<h3 class="text-neutral-900 font-bold mb-4">操作日志</h3>
<div class="space-y-5">
<div
v-for="(log, index) in order.operationLogs"
v-for="(log, index) in logData"
:key="log.id"
class="flex gap-4"
>
<div class="flex flex-col items-center">
<div class="w-3 h-3 rounded-full bg-brand-primary"></div>
<div
v-if="index < order.operationLogs!.length - 1"
v-if="index < logData!.length - 1"
class="w-0.5 flex-1 bg-neutral-300 my-1"
></div>
</div>
<div class="flex-1 pb-2">
<div class="flex items-center gap-2 mb-1.5">
<span class="text-neutral-900">{{ log.action }}</span>
<el-tag size="small" type="info">{{ log.operator }}</el-tag>
<span class="text-neutral-900">{{ log.functionName }}</span>
<el-tag size="small" type="info">{{ log.operatorName }}</el-tag>
</div>
<p class="text-sm text-neutral-600 mb-1">{{ log.details }}</p>
<p class="text-xs text-neutral-500">{{ log.time }}</p>
<p class="text-sm text-neutral-600 mb-1">{{ log.detail.memo || log.detail.auditMemo}}</p>
<p class="text-xs text-neutral-500">{{ $utils.detailTime(log.createTime) }}</p>
</div>
</div>
</div>
</el-card>
<!-- 订单关闭信息 -->
<el-card v-if="order.businessStatus === '已关闭' && order.closeReason" class="p-6 bg-neutral-50">
<el-card v-if="order.status === '0' && order.closeReason" class="p-6 bg-neutral-50">
<h3 class="text-neutral-900 font-bold mb-4">订单关闭信息</h3>
<div>
<label class="text-neutral-700 text-sm">订单关闭原因</label>
......@@ -218,11 +160,11 @@
</label>
<el-input
v-if="canEditCompleteInfo"
v-model="crmOrderNumber"
v-model="crmOrderId"
placeholder="请输入CRM订单编号"
class="mt-2"
/>
<p v-else class="mt-2 text-neutral-700">{{ order.crmOrderNumber || '-' }}</p>
<p v-else class="mt-2 text-neutral-700">{{ order.crmOrderId || '-' }}</p>
</div>
<div>
......@@ -237,57 +179,59 @@
/>
<p v-else class="mt-2 text-neutral-700">{{ order.processRemark || '-' }}</p>
</div>
<div v-if="!canEditCompleteInfo">
<label class="text-neutral-900 text-sm">办结登记时间</label>
<p class="mt-2 text-neutral-700">{{ order.completeTime || '-' }}</p>
</div>
<el-button
type="primary"
@click="handleSaveCrm"
v-if="canEditCompleteInfo"
class="bg-brand-primary"
style="float: right;"
>
保存
</el-button>
</div>
</el-card>
<!-- 酬金信息区 -->
<el-card v-if="order.businessStatus !== '待办理'" class="p-6">
<el-card class="p-6">
<h3 class="text-neutral-900 font-bold mb-4">酬金信息</h3>
<div class="space-y-5">
<!-- 预计酬金金额 -->
<div>
<label class="text-neutral-500 text-sm">预计酬金金额</label>
<p class="mt-2 text-2xl text-neutral-900">
¥{{ order.estimatedReward?.toFixed(2) || '0.00' }}
¥{{ order.preMoney?.toFixed(2) || '0.00' }}
</p>
</div>
<!-- 实际结酬金额 -->
<div>
<label class="text-neutral-900 text-sm">
实际结酬金额{{ canEditReward && order.reviewStatus !== '待审核' ? '(选填)' : '' }}
实际结酬金额{{ canEditReward && order.auditStatus !== '0' ? '(选填)' : '' }}
</label>
<el-input
v-if="canEditReward && order.reviewStatus !== '待审核'"
v-model="actualReward"
v-if="canEditReward && order.auditStatus !== '0'"
v-model="realMoney"
type="Number"
placeholder="请输入实际结酬金额"
class="mt-2"
>
<template #prepend>¥</template>
</el-input>
<p v-else class="mt-2 text-2xl text-success">
¥{{ order.actualReward?.toFixed(2) || '0.00' }}
¥{{ order.realMoney?.toFixed(2) || '0.00' }}
</p>
</div>
<!-- 审核通过时间 -->
<div v-if="order.reviewStatus === '审核通过' && order.paymentTime">
<label class="text-neutral-500 text-sm">审核通过时间</label>
<p class="mt-2 text-neutral-900">{{ order.paymentTime }}</p>
</div>
<!-- 审核驳回原因 -->
<div v-if="order.rejectReason">
<label class="text-error text-sm">审核驳回原因</label>
<div class="mt-3 p-3 bg-orange-50 border border-orange-200 rounded">
<p class="text-sm text-orange-900">{{ order.rejectReason }}</p>
</div>
</div>
<el-button
type="primary"
@click="handleSaveReward"
v-if="canEditReward"
class="bg-brand-primary"
style="float: right;"
>
提交审核
</el-button>
</div>
</el-card>
</div>
......@@ -378,14 +322,16 @@
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed ,getCurrentInstance,onMounted} from 'vue'
import { ElMessage } from 'element-plus'
import { ArrowLeft, Check, X } from 'lucide-vue-next'
import { ArrowDown } from '@element-plus/icons-vue'
const { $api,$utils } = getCurrentInstance()!.appContext.config.globalProperties
// 类型定义
type BusinessStatus = '待办理' | '办理成功' | '已关闭'
type ReviewStatus = '待审核' | '审核通过' | '审核驳回'
type status = '-1' | '1' | '0'
type auditStatus = '0' | '1' | '2'
interface OperationLog {
id: string
......@@ -397,16 +343,18 @@ interface OperationLog {
interface Order {
id: string
registerPersonPhone: string
registerPersonName: string
chinaPersonPhone: string
chinaPersonName: string
customerPhone: string
businessName: string
registerTime: string
businessStatus: BusinessStatus
reviewStatus?: ReviewStatus
estimatedReward?: number
actualReward?: number
crmOrderNumber?: string
job: string
jobName?: string
memo?: string
createTime: string
status: status
auditStatus?: auditStatus
preMoney?: number
realMoney?: number
crmOrderId?: string
completeTime?: string
paymentTime?: string
rejectReason?: string
......@@ -419,8 +367,8 @@ interface Order {
interface BusinessRule {
id: string
businessCode: string
businessName: string
estimatedReward: number
jobName: string
preMoney: number
status: string
createTime: string
updateTime?: string
......@@ -442,12 +390,37 @@ const emit = defineEmits<{
}>()
// 响应式数据
const crmOrderNumber = ref(props.order.crmOrderNumber || '')
const crmOrderId = ref(props.order.crmOrderId || '')
const processRemark = ref(props.order.processRemark || '')
const actualReward = ref(
props.order.actualReward !== undefined ? String(props.order.actualReward) : ''
const realMoney = ref(
props.order.realMoney !== undefined ? String(props.order.realMoney) : ''
)
onMounted(()=>{
queryLog()
})
const logData = ref<any[]>([])
const queryLog = async ()=>{
try {
const response = await $api.queryOrderLog({
id: props.order.id
})
if (response.c === 0) {
response.d.forEach((item: any)=>{
item.detail = JSON.parse(item.parameter)
})
logData.value = response.d
} else {
ElMessage.error(response.m)
}
} catch (error) {
}
}
// 对话框状态
const isRejectDialogOpen = ref(false)
const rejectReason = ref('')
......@@ -456,100 +429,114 @@ const closeReason = ref('')
// 计算属性
const canEditCompleteInfo = computed(() => {
return (props.order.businessStatus === '待办理' || props.order.businessStatus === '办理成功') &&
props.order.reviewStatus !== '审核通过' &&
props.order.reviewStatus !== '待审核'
let p = props.order
return p.status==='-1' || (p.status==='1' && p.auditStatus!=='0' && p.auditStatus!=='1')
})
const canEditReward = computed(() => {
return (props.order.businessStatus === '待办理' || props.order.businessStatus === '办理成功') &&
props.order.reviewStatus !== '审核通过'
let p = props.order
return p.status==='1' && p.auditStatus!=='0' && p.auditStatus!=='1'
})
// 方法
// 状态样式映射 - 使用浅色背景样式(与订单监控页保持一致)
const getBusinessStatusClass = (status: BusinessStatus) => {
const getstatusClass = (status: status) => {
const statusMap = {
'待办理': 'bg-blue-100 text-blue-800',
'办理成功': 'bg-green-100 text-green-800',
'已关闭': 'bg-gray-100 text-gray-800'
'-1': 'bg-blue-100 text-blue-800',
'1': 'bg-green-100 text-green-800',
'0': 'bg-gray-100 text-gray-800'
}
return statusMap[status] || 'bg-gray-100 text-gray-800'
}
const getstatusName = (status: status) => {
const statusMap = {
'-1': '待办理',
'1': '办理成功',
'0': '已关闭'
}
return statusMap[status] || '--'
}
const getReviewStatusClass = (status: ReviewStatus) => {
const getauditStatusClass = (status: auditStatus) => {
const statusMap = {
'待审核': 'bg-purple-100 text-purple-800',
'审核通过': 'bg-green-100 text-green-800',
'审核驳回': 'bg-orange-100 text-orange-800'
'0': 'bg-purple-100 text-purple-800',
'1': 'bg-green-100 text-green-800',
'2': 'bg-orange-100 text-orange-800'
}
return statusMap[status] || 'bg-gray-100 text-gray-800'
}
const getauditStatusName = (status: auditStatus) => {
const statusMap = {
'0': '待审核',
'1': '审核通过',
'2': '审核驳回'
}
return statusMap[status] || '--'
}
// 待办理状态提交审核
const handleSaveCompleteInfo = () => {
if (!crmOrderNumber.value.trim()) {
// crm保存
const handleSaveCrm = async ()=>{
if (!crmOrderId.value.trim()) {
ElMessage.error('请填写CRM订单编号')
return
}
const currentTime = new Date().toLocaleString('zh-CN')
// 自动关联业务规则,获取预计酬金
const businessRule = props.businessRules.find(rule =>
rule.businessName === props.order.businessName && rule.status === '生效中'
)
const response = await $api.updateOrderCrm({
id: props.order.id,
crmOrderId: crmOrderId.value,
memo: processRemark.value
})
// 实际发放酬金为选填项,未填写则使用预计酬金
let finalAmount = businessRule?.estimatedReward || 0
if (actualReward.value && actualReward.value.trim()) {
const amount = parseFloat(actualReward.value)
if(response.c === 0){
ElMessage.success('保存成功')
setTimeout(() => {
emit('back')
}, 1000)
}else{
ElMessage.error(response.m)
}
}
const handleSaveReward = async () => {
if (realMoney.value && realMoney.value.trim()) {
const amount = parseFloat(realMoney.value)
if (isNaN(amount) || amount < 0) {
ElMessage.error('请输入有效的实际发放酬金')
return
}
finalAmount = amount
}
const newLog = {
id: `LOG${Date.now()}`,
time: currentTime,
operator: '管理员',
action: '提交审核',
details: `CRM订单编号:${crmOrderNumber.value},提交给主管理员审核,实际发放酬金:¥${finalAmount.toFixed(2)}`
}
emit('update', {
crmOrderNumber: crmOrderNumber.value,
processRemark: processRemark.value.trim(),
completeTime: currentTime,
businessStatus: '办理成功',
reviewStatus: '待审核',
estimatedReward: businessRule?.estimatedReward,
actualReward: finalAmount,
rejectReason: undefined,
operationLogs: [...(props.order.operationLogs || []), newLog]
const response = await $api.updateOrderMoney({
list: [{
id: props.order.id,
realMoney: realMoney.value
}]
})
ElMessage.success('已提交审核,等待主管理员审核')
if(response.c === 0){
ElMessage.success('提交成功')
setTimeout(() => {
emit('back')
}, 1000)
}else{
ElMessage.error(response.m)
}
setTimeout(() => {
emit('back')
}, 1000)
}
// 提交审核(办理成功/审核驳回 → 待审核)
const handleSubmitReview = () => {
if (!crmOrderNumber.value.trim()) {
if (!crmOrderId.value.trim()) {
ElMessage.error('请填写CRM订单编号')
return
}
let finalAmount = props.order.estimatedReward || 0
let finalAmount = props.order.preMoney || 0
if (actualReward.value && actualReward.value.trim()) {
const amount = parseFloat(actualReward.value)
if (realMoney.value && realMoney.value.trim()) {
const amount = parseFloat(realMoney.value)
if (isNaN(amount) || amount < 0) {
ElMessage.error('请输入有效的实际发放酬金')
return
......@@ -562,14 +549,14 @@ const handleSubmitReview = () => {
time: new Date().toLocaleString('zh-CN'),
operator: '管理员',
action: '提交审核',
details: `CRM订单编号:${crmOrderNumber.value},提交给主管理员审核,实际发放酬金:¥${finalAmount.toFixed(2)}`
details: `CRM订单编号:${crmOrderId.value},提交给主管理员审核,实际发放酬金:¥${finalAmount.toFixed(2)}`
}
emit('update', {
crmOrderNumber: crmOrderNumber.value,
crmOrderId: crmOrderId.value,
processRemark: processRemark.value.trim(),
reviewStatus: '待审核',
actualReward: finalAmount,
auditStatus: '0',
realMoney: finalAmount,
rejectReason: undefined,
operationLogs: [...(props.order.operationLogs || []), newLog]
})
......@@ -582,87 +569,87 @@ const handleSubmitReview = () => {
}
// 审核通过
const handleApprove = () => {
const currentTime = new Date().toLocaleString('zh-CN')
const newLog = {
id: `LOG${Date.now()}`,
time: currentTime,
operator: '主管理员',
action: '审核通过',
details: `主管审核通过,金额:¥${props.order.actualReward?.toFixed(2)}`
const handleApprove = async () => {
try {
const response = await $api.audioOrderList({
list:[{
id: props.order.id,
auditStatus: '1'
}]
})
if (response.c === 0) {
ElMessage.success('审核通过!')
setTimeout(() => {
emit('back')
}, 1000)
} else {
ElMessage.error(response.m)
}
} catch (error) {
}
emit('update', {
reviewStatus: '审核通过',
paymentTime: currentTime,
operationLogs: [...(props.order.operationLogs || []), newLog]
})
ElMessage.success('审核通过!')
setTimeout(() => {
emit('back')
}, 1000)
}
// 审核驳回
const handleReject = () => {
const handleReject = async () => {
if (!rejectReason.value.trim()) {
ElMessage.error('请填写驳回原因')
return
}
const newLog = {
id: `LOG${Date.now()}`,
time: new Date().toLocaleString('zh-CN'),
operator: '主管理员',
action: '审核驳回',
details: `驳回原因:${rejectReason.value}`
try {
const response = await $api.audioOrderList({
list:[{
id: props.order.id,
auditStatus: '2',
auditMemo: rejectReason.value
}]
})
if (response.c === 0) {
ElMessage.success('已驳回订单')
isRejectDialogOpen.value = false
setTimeout(()=>{
rejectReason.value = ''
},0)
setTimeout(() => {
emit('back')
}, 1000)
} else {
ElMessage.error(response.m)
}
} catch (error) {
}
emit('update', {
reviewStatus: '审核驳回',
rejectReason: rejectReason.value,
operationLogs: [...(props.order.operationLogs || []), newLog]
})
ElMessage.success('已驳回订单')
isRejectDialogOpen.value = false
rejectReason.value = ''
setTimeout(() => {
emit('back')
}, 1000)
}
// 关闭订单
const handleClose = () => {
const handleClose = async () => {
if (!closeReason.value.trim()) {
ElMessage.error('请填写关闭原因')
return
}
const newLog = {
id: `LOG${Date.now()}`,
time: new Date().toLocaleString('zh-CN'),
operator: '管理员',
action: '关闭订单',
details: `关闭原因:${closeReason.value}`
}
emit('update', {
businessStatus: '已关闭',
closeReason: closeReason.value,
operationLogs: [...(props.order.operationLogs || []), newLog]
const response = await $api.closeOrder({
id: props.order.id,
memo: closeReason.value
})
ElMessage.success('订单已关闭')
isCloseDialogOpen.value = false
closeReason.value = ''
setTimeout(() => {
emit('back')
}, 1000)
if(response.c === 0){
ElMessage.success('订单已关闭')
isCloseDialogOpen.value = false
closeReason.value = ''
setTimeout(() => {
emit('back')
}, 1000)
}else{
ElMessage.error(response.m)
}
}
const handleAuditCommand = (command: string) => {
......
......@@ -32,11 +32,21 @@
<el-card>
<div class="flex items-center gap-2 w-full p-6">
<!-- 搜索框 -->
<div class="relative flex-[2]">
<div class="relative" style="width: 200px;">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="searchKeyword"
placeholder="输入订单ID、CRM订单编号、能人手机号、客户号码搜索"
v-model="customerPhone"
placeholder="客户号码"
class="search-input text-ellipsis-input"
clearable
/>
</div>
<div class="relative" style="width: 200px;">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="chinaPersonPhone"
placeholder="登记人手机号"
class="search-input text-ellipsis-input"
clearable
/>
......@@ -57,28 +67,28 @@
<!-- 全部业务状态 -->
<el-select v-model="businessStatusFilter" placeholder="全部业务状态" class="flex-1 bg-gray-100">
<el-option label="全部业务状态" value="all" />
<el-option label="待办理" value="待办理" />
<el-option label="办理成功" value="办理成功" />
<el-option label="已关闭" value="已关闭" />
<el-option label="全部业务状态" value="" />
<el-option label="待办理" value="-1" />
<el-option label="办理成功" value="1" />
<el-option label="已关闭" value="0" />
</el-select>
<!-- 全部审核状态 -->
<el-select v-model="reviewStatusFilter" placeholder="全部审核状态" class="flex-1 bg-gray-100">
<el-option label="全部审核状态" value="all" />
<el-option label="待审核" value="待审核" />
<el-option label="审核通过" value="审核通过" />
<el-option label="审核驳回" value="审核驳回" />
<el-option label="全部审核状态" value="" />
<el-option label="待审核" value="0" />
<el-option label="审核通过" value="1" />
<el-option label="审核驳回" value="2" />
</el-select>
<!-- 全部业务 -->
<el-select v-model="businessNameFilter" placeholder="全部业务" class="flex-1 bg-gray-100">
<el-option label="全部业务" value="all" />
<el-option label="全部业务" value="" />
<el-option
v-for="businessName in uniqueBusinessNames"
:key="businessName"
:label="businessName"
:value="businessName"
:key="businessName.jobId"
:label="businessName.jobName"
:value="businessName.jobId"
/>
</el-select>
......@@ -106,87 +116,84 @@
<el-table-column type="selection" width="50" :selectable="isRowSelectable" />
<!-- 订单ID -->
<el-table-column prop="id" label="订单ID" width="140" show-overflow-tooltip>
<el-table-column prop="id" label="订单ID" width="200" show-overflow-tooltip>
<template #default="{ row }">
<span class="font-mono text-[14px]">{{ row.id }}</span>
</template>
</el-table-column>
<!-- 能人 -->
<el-table-column prop="registerPersonName" label="能人" width="70" />
<el-table-column prop="chinaPersonName" label="能人" width="70" />
<!-- 能人手机号 -->
<el-table-column prop="registerPersonPhone" label="能人手机号" width="110" show-overflow-tooltip>
<el-table-column prop="chinaPersonPhone" label="能人手机号" width="130" show-overflow-tooltip>
<template #default="{ row }">
<span class="font-mono">{{ row.registerPersonPhone }}</span>
<span class="font-mono">{{ row.chinaPersonPhone }}</span>
</template>
</el-table-column>
<!-- 客户号码 -->
<el-table-column prop="customerPhone" label="客户号码" width="110" show-overflow-tooltip>
<el-table-column prop="customerPhone" label="客户号码" width="130" show-overflow-tooltip>
<template #default="{ row }">
<span class="font-mono">{{ row.customerPhone }}</span>
</template>
</el-table-column>
<!-- 业务名称 -->
<el-table-column prop="businessName" label="业务名称" min-width="100" />
<el-table-column prop="jobName" label="业务名称" min-width="100" />
<!-- 登记时间 -->
<el-table-column prop="registerTime" label="登记时间" width="140" show-overflow-tooltip />
<!-- 预计酬金 -->
<el-table-column prop="estimatedReward" label="预计酬金" width="90" align="right">
<el-table-column prop="createTime" label="登记时间" min-width="140" show-overflow-tooltip>
<template #default="{ row }">
<span class="font-mono">¥{{ row.estimatedReward.toFixed(2) }}</span>
<span class="font-mono">{{ $utils.detailTime(row.createTime) }}</span>
</template>
</el-table-column>
<!-- 实际酬金 -->
<el-table-column prop="actualReward" label="实际酬金" width="90" align="right">
<!-- 预计酬金 -->
<el-table-column prop="preMoney" label="预计酬金" width="90" align="right">
<template #default="{ row }">
<span v-if="row.actualReward" class="font-mono">¥{{ row.actualReward.toFixed(2) }}</span>
<span v-else class="text-neutral-400">-</span>
<span v-if="row.preMoney" class="font-mono">¥{{ row.preMoney }}</span>
<span v-else class="text-neutral-400">--</span>
</template>
</el-table-column>
<!-- CRM订单编号 -->
<el-table-column prop="crmOrderNumber" label="CRM订单编号" min-width="120" show-overflow-tooltip>
<!-- 实际酬金 -->
<el-table-column prop="realMoney" label="实际酬金" width="90" align="right">
<template #default="{ row }">
<span v-if="row.crmOrderNumber" class="font-mono text-[14px]">{{ row.crmOrderNumber }}</span>
<span v-else class="text-neutral-400">-</span>
<span v-if="row.realMoney" class="font-mono">¥{{ row.realMoney }}</span>
<span v-else class="text-neutral-400">--</span>
</template>
</el-table-column>
<!-- 办结登记时间 -->
<el-table-column prop="completeTime" label="办结登记时间" min-width="140" show-overflow-tooltip>
<!-- CRM订单编号 -->
<el-table-column prop="crmOrderId" label="CRM订单编号" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.completeTime">{{ row.completeTime }}</span>
<span v-else class="text-neutral-400">-</span>
<span v-if="row.crmOrderId" class="font-mono text-[14px]">{{ row.crmOrderId }}</span>
<span v-else class="text-neutral-400">--</span>
</template>
</el-table-column>
<!-- 业务状态 -->
<el-table-column prop="businessStatus" label="业务状态" width="90" fixed="right">
<el-table-column prop="status" label="业务状态" width="90" fixed="right">
<template #default="{ row }">
<span
:class="getBusinessStatusClass(row.businessStatus)"
:class="getBusinessStatusClass(row.status)"
class="px-2 py-1 rounded text-xs font-medium"
>
{{ row.businessStatus }}
{{ getBusinessStatusName(row.status) }}
</span>
</template>
</el-table-column>
<!-- 审核状态 -->
<el-table-column prop="reviewStatus" label="审核状态" width="90" fixed="right">
<el-table-column prop="auditStatus" label="审核状态" width="90" fixed="right">
<template #default="{ row }">
<span
v-if="row.reviewStatus"
:class="getReviewStatusClass(row.reviewStatus)"
v-if="row.auditStatus"
:class="getReviewStatusClass(row.auditStatus)"
class="px-2 py-1 rounded text-xs font-medium"
>
{{ row.reviewStatus }}
{{ getReviewStatusName(row.auditStatus) }}
</span>
<span v-else class="text-neutral-400">-</span>
</template>
......@@ -205,7 +212,7 @@
详情
</el-button>
<el-button
v-if="row.reviewStatus === '待审核'"
v-if="row.auditStatus === 0"
type="primary"
link
size="small"
......@@ -222,15 +229,15 @@
<!-- 分页 -->
<div class="flex justify-between items-center mt-4 py-3 px-6">
<div class="text-sm text-neutral-600">
{{ filteredOrders.length }} 条数据,当前第 {{ currentPage }}/{{ totalPages }}
{{ totalElements }} 条数据,当前第 {{ currentPage }}/{{ totalPages }}
<span v-if="selectedOrderIds.length > 0" class="ml-2">
,已选择 {{ selectedOrderIds.length }}
</span>
</div>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
<span class="text-sm text-neutral-600">每页条数</span>
<el-select v-model="pageSize" class="w-[180px]" size="small" @change="handlePageSizeChange">
<span class="text-sm text-neutral-600" style="width: 60px;">每页条数</span>
<el-select v-model="pageSize" class="w-[180px]" style="width: 90px;" size="small" @change="handlePageSizeChange">
<el-option label="10 条/页" :value="10" />
<el-option label="20 条/页" :value="20" />
<el-option label="50 条/页" :value="50" />
......@@ -240,7 +247,7 @@
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="filteredOrders.length"
:total="totalElements"
layout="prev, pager, next"
small
@current-change="handleCurrentChange"
......@@ -251,9 +258,7 @@
<!-- 审核对话框 -->
<el-dialog v-model="isReviewDialogOpen" title="订单审核" width="600px" class="single-review-dialog">
<div v-if="reviewingOrder" class="space-y-4">
<div class="space-y-4">
<div class="grid grid-cols-2 gap-6">
<!-- 左列 -->
<div class="space-y-4">
......@@ -263,15 +268,15 @@
</div>
<div class="flex items-center">
<label class="text-sm text-neutral-500 shrink-0">能人</label>
<p class="text-sm text-neutral-900" style="margin-left: 16px;">{{ reviewingOrder.registerPersonName }}</p>
<p class="text-sm text-neutral-900" style="margin-left: 16px;">{{ reviewingOrder.chinaPersonName }}</p>
</div>
<div class="flex items-center">
<label class="text-sm text-neutral-500 shrink-0">CRM订单编号</label>
<p class="text-sm text-neutral-900 font-mono" style="margin-left: 16px;">{{ reviewingOrder.crmOrderNumber || '-' }}</p>
<p class="text-sm text-neutral-900 font-mono" style="margin-left: 16px;">{{ reviewingOrder.crmOrderId || '-' }}</p>
</div>
<div class="flex items-center">
<label class="text-sm text-neutral-500 shrink-0">实际酬金</label>
<p class="text-sm font-mono text-green-600" style="margin-left: 16px;">¥{{ (reviewingOrder.actualReward || reviewingOrder.estimatedReward).toFixed(2) }}</p>
<p class="text-sm font-mono text-green-600" style="margin-left: 16px;">¥{{ reviewingOrder.realMoney?.toFixed(2) || '--' }}</p>
</div>
</div>
......@@ -279,7 +284,7 @@
<div class="space-y-4">
<div class="flex items-center">
<label class="text-sm text-neutral-500 shrink-0">业务名称</label>
<p class="text-sm text-neutral-900" style="margin-left: 16px;">{{ reviewingOrder.businessName }}</p>
<p class="text-sm text-neutral-900" style="margin-left: 16px;">{{ reviewingOrder.jobName }}</p>
</div>
<div class="flex items-center">
<label class="text-sm text-neutral-500 shrink-0">客户号码</label>
......@@ -287,7 +292,7 @@
</div>
<div class="flex items-center">
<label class="text-sm text-neutral-500 shrink-0">预计酬金</label>
<p class="text-sm font-mono text-blue-600" style="margin-left: 16px;">¥{{ reviewingOrder.estimatedReward.toFixed(2) }}</p>
<p class="text-sm font-mono text-blue-600" style="margin-left: 16px;">¥{{ reviewingOrder.preMoney?.toFixed(2) || '--' }}</p>
</div>
</div>
</div>
......@@ -330,10 +335,10 @@
<div class="flex-1">
<p class="text-sm font-medium text-neutral-900">订单ID: {{ order.id }}</p>
<p class="text-xs text-neutral-600 mt-1">
业务类型:{{ order.businessName }} |
预计酬金:¥{{ order.estimatedReward.toFixed(2) }}
<span v-if="order.actualReward">
| 实际酬金:¥{{ order.actualReward.toFixed(2) }}
业务类型:{{ order.jobName }} |
预计酬金:¥{{ order.preMoney?.toFixed(2)||'--' }}
<span v-if="order.realMoney">
| 实际酬金:¥{{ order.realMoney.toFixed(2) }}
</span>
</p>
</div>
......@@ -434,8 +439,6 @@
</el-button>
</div>
</div>
</div>
</div>
......@@ -448,25 +451,21 @@
</div>
<el-table :data="batchPreviewRows" height="260" border stripe>
<el-table-column prop="orderId" label="订单ID" min-width="160" />
<el-table-column prop="currentReward" label="当前酬金" width="100" align="right">
<template #default="{ row }">
<span class="font-mono">¥{{ Number(row.currentReward).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column prop="newReward" label="新酬金" width="100" align="right">
<el-table-column prop="id" label="订单ID" min-width="160" />
<el-table-column prop="realMoney" label="实际酬金" width="100" align="right">
<template #default="{ row }">
<span class="font-mono">¥{{ Number(row.newReward).toFixed(2) }}</span>
<span class="font-mono">¥{{ Number(row.realMoney).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip />
<el-table-column prop="id" label="业务代码" min-width="160" />
<el-table-column prop="id" label="业务名称" min-width="160" />
<el-table-column prop="status" label="校验" width="80">
<template #default="{ row }">
<el-tag v-if="row.status === 'OK'" type="success" size="small">有效</el-tag>
<el-tag v-if="!row.memo" type="success" size="small">有效</el-tag>
<el-tag v-else type="danger" size="small">无效</el-tag>
</template>
</el-table-column>
<el-table-column prop="error" label="错误信息" min-width="180" show-overflow-tooltip />
<el-table-column prop="memo" label="错误信息" min-width="180" show-overflow-tooltip />
</el-table>
</div>
</div>
......@@ -474,10 +473,10 @@
<el-button @click="closeBatchDialog">取消</el-button>
<el-button
type="primary"
:disabled="!batchParseDone || batchValidRows.length === 0"
:disabled="!batchParseDone || batchInvalidRows.length>0"
@click="openVerifyDialog"
>
提交审核{{ batchValidRows.length }}
提交审核
</el-button>
</template>
</el-dialog>
......@@ -545,10 +544,12 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { ref, computed, onMounted, watch , getCurrentInstance} from 'vue'
import { ElMessage } from 'element-plus'
import { Download, Check, Edit3, Search, Upload, X } from 'lucide-vue-next'
const { $api,$utils } = getCurrentInstance()!.appContext.config.globalProperties
// Props
interface Props {
orders?: any[]
......@@ -564,11 +565,12 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits(['view-order-detail', 'update-order', 'orders-update'])
// 响应式数据
const searchKeyword = ref('')
const customerPhone = ref('')
const chinaPersonPhone = ref('')
const dateRange = ref<[string, string] | null>(null)
const businessStatusFilter = ref('all')
const reviewStatusFilter = ref('all')
const businessNameFilter = ref('all')
const businessStatusFilter = ref('')
const reviewStatusFilter = ref('')
const businessNameFilter = ref('')
const selectedOrderIds = ref<string[]>([])
const currentPage = ref(1)
const pageSize = ref(20)
......@@ -598,8 +600,8 @@ const verifyCode = ref('')
const orders = ref<any[]>([])
// 类型定义
type BusinessStatus = '待办理' | '办理成功' | '已关闭'
type ReviewStatus = '待审核' | '审核通过' | '审核驳回'
type BusinessStatus = '待办理' | '办理成功' | '已关闭' | '-1' | '0' | '1'
type ReviewStatus = '待审核' | '审核通过' | '审核驳回' | '0' | '1' | '2'
interface OperationLog {
id: string
......@@ -630,246 +632,80 @@ interface Order {
operationLogs?: OperationLog[]
}
// 生成模拟数据
const generateMockOrders = (): Order[] => {
const mockOrders: Order[] = []
const names = [
'王芳', '李静', '刘备', '关羽', '张飞', '赵云', '马超', '黄忠', '魏延', '姜维',
'陈明', '李娜', '王强', '赵丽', '孙杰', '周芳', '吴军', '郑敏', '刘洋', '黄涛',
'杨雪', '朱鹏', '徐霞', '何亮', '高华', '林静', '梁伟', '宋佳', '韩冰', '唐磊',
'冯琳', '许刚', '曹丽', '蔡勇', '彭燕', '马强', '卢敏', '陆伟', '姚娟', '钱浩',
'秦霞', '任杰', '沈芳', '汤明', '田丽', '万强', '温娜', '夏伟', '谢静', '熊勇'
]
const businesses = ['5G套餐办理', '宽带新装', '话费充值', '流量包办理']
const businessRewards = { '5G套餐办理': 50, '宽带新装': 80, '话费充值': 30, '流量包办理': 40 }
const businessStatuses: BusinessStatus[] = ['待办理', '办理成功', '已关闭']
const reviewStatuses: ReviewStatus[] = ['待审核', '审核通过', '审核驳回']
for (let i = 0; i < 120; i++) {
const dayOffset = Math.floor(i / 10)
const date = new Date(2025, 9, 28 - dayOffset)
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
const orderId = `ORD${dateStr.replace(/-/g, '')}${String(i + 1).padStart(3, '0')}`
const name = names[i % names.length] as string
const phone = `138${String(10000 + i).padStart(8, '0')}`
const customerPhone = `139${String(20000 + i).padStart(8, '0')}`
const businessName = businesses[i % businesses.length] as string
const estimatedReward = businessRewards[businessName as keyof typeof businessRewards]
// 根据索引分配不同的状态
const statusType = i % 6
let businessStatus: BusinessStatus
let reviewStatus: ReviewStatus | undefined
let actualReward: number | undefined
let crmOrderNumber: string | undefined
let completeTime: string | undefined
let paymentTime: string | undefined
let rejectReason: string | undefined
let closeReason: string | undefined
const operationLogs: OperationLog[] = []
if (statusType === 0) {
// 待办理 (约17%)
businessStatus = '待办理'
} else if (statusType === 1) {
// 办理成功+待审核 (约17%)
businessStatus = '办理成功'
reviewStatus = '待审核'
actualReward = estimatedReward + (Math.random() > 0.5 ? Math.floor(Math.random() * 10) : -Math.floor(Math.random() * 5))
crmOrderNumber = `CRM${dateStr.replace(/-/g, '')}${String(i + 1000).padStart(4, '0')}`
completeTime = `${dateStr} ${String(14 + (i % 8)).padStart(2, '0')}:${String((i * 7) % 60).padStart(2, '0')}`
} else if (statusType === 2) {
// 办理成功+审核通过 (约17%)
businessStatus = '办理成功'
reviewStatus = '审核通过'
actualReward = estimatedReward + (Math.random() > 0.7 ? Math.floor(Math.random() * 5) : 0)
crmOrderNumber = `CRM${dateStr.replace(/-/g, '')}${String(i + 1000).padStart(4, '0')}`
completeTime = `${dateStr} ${String(14 + (i % 8)).padStart(2, '0')}:${String((i * 7) % 60).padStart(2, '0')}`
paymentTime = `${dateStr} ${String(16 + (i % 6)).padStart(2, '0')}:${String((i * 17) % 60).padStart(2, '0')}`
} else if (statusType === 3) {
// 办理成功+审核驳回 (约17%)
businessStatus = '办理成功'
reviewStatus = '审核驳回'
actualReward = estimatedReward + (i % 3 === 0 ? 25 : i % 3 === 1 ? -15 : 10)
crmOrderNumber = `CRM${dateStr.replace(/-/g, '')}${String(i + 1000).padStart(4, '0')}`
completeTime = `${dateStr} ${String(14 + (i % 8)).padStart(2, '0')}:${String((i * 7) % 60).padStart(2, '0')}`
rejectReason = ['金额与实际不符', 'CRM订单号有误', '缺少必要凭证', '客户信息不匹配'][i % 4]
} else if (statusType === 4) {
// 已关闭 (约17%)
businessStatus = '已关闭'
actualReward = estimatedReward
crmOrderNumber = `CRM${dateStr.replace(/-/g, '')}${String(i + 1000).padStart(4, '0')}`
completeTime = `${dateStr} ${String(14 + (i % 8)).padStart(2, '0')}:${String((i * 7) % 60).padStart(2, '0')}`
closeReason = ['客户取消办理', '业务规则变更', '重复订单', '客户联系不上'][i % 4]
} else {
// 办理成功无审核状态 (约15%)
businessStatus = '办理成功'
actualReward = estimatedReward
crmOrderNumber = `CRM${dateStr.replace(/-/g, '')}${String(i + 1000).padStart(4, '0')}`
completeTime = `${dateStr} ${String(14 + (i % 8)).padStart(2, '0')}:${String((i * 7) % 60).padStart(2, '0')}`
}
// 添加基础操作日志
operationLogs.push({
id: `LOG${orderId}_001`,
time: `${dateStr} ${String(9 + (i % 4)).padStart(2, '0')}:${String((i * 13) % 60).padStart(2, '0')}`,
operator: name,
action: '登记订单',
details: `能人登记新订单,等待办理。业务类型:${businessName}`
})
// 根据状态添加相应的操作日志
if (businessStatus === '办理成功') {
operationLogs.push({
id: `LOG${orderId}_002`,
time: completeTime!,
operator: '管理员',
action: '保存办结信息',
details: `CRM订单编号:${crmOrderNumber},移动人员成功办理业务`
})
if (reviewStatus === '审核通过') {
operationLogs.push({
id: `LOG${orderId}_003`,
time: `${dateStr} ${String(16 + (i % 6)).padStart(2, '0')}:${String((i * 17) % 60).padStart(2, '0')}`,
operator: '主管理员',
action: '审核通过',
details: `主管审核通过,金额:¥${actualReward?.toFixed(2)}`
})
} else if (reviewStatus === '审核驳回') {
const rejectReasons = ['金额与实际不符', 'CRM订单号有误', '缺少必要凭证', '客户信息不匹配']
const rejectReason = rejectReasons[i % rejectReasons.length]
operationLogs.push({
id: `LOG${orderId}_003`,
time: `${dateStr} ${String(16 + (i % 6)).padStart(2, '0')}:${String((i * 17) % 60).padStart(2, '0')}`,
operator: '主管理员',
action: '审核驳回',
details: `驳回原因:${rejectReason}`
})
}
} else if (businessStatus === '已关闭') {
const closeReasons = ['客户取消办理', '业务规则变更', '重复订单', '客户联系不上']
const closeReason = closeReasons[i % closeReasons.length]
operationLogs.push({
id: `LOG${orderId}_002`,
time: completeTime!,
operator: '管理员',
action: '关闭订单',
details: `关闭原因:${closeReason}`
})
}
mockOrders.push({
id: orderId,
registerPersonPhone: phone,
registerPersonName: name,
customerPhone: customerPhone,
businessName: businessName,
registerTime: `${dateStr} ${String(9 + (i % 4)).padStart(2, '0')}:${String((i * 13) % 60).padStart(2, '0')}`,
businessStatus: businessStatus,
reviewStatus: reviewStatus,
estimatedReward: estimatedReward,
actualReward: actualReward,
crmOrderNumber: crmOrderNumber,
completeTime: completeTime,
paymentTime: reviewStatus === '审核通过' ? `${dateStr} ${String(16 + (i % 6)).padStart(2, '0')}:${String((i * 17) % 60).padStart(2, '0')}` : undefined,
rejectReason: reviewStatus === '审核驳回' ? ['金额与实际不符', 'CRM订单号有误', '缺少必要凭证', '客户信息不匹配'][i % 4] : undefined,
closeReason: businessStatus === '已关闭' ? ['客户取消办理', '业务规则变更', '重复订单', '客户联系不上'][i % 4] : undefined,
remarks: i % 8 === 0 ? '客户要求尽快办理' : i % 12 === 0 ? '客户地址偏远' : undefined,
processRemark: businessStatus !== '待办理' ? `办理备注信息${i + 1}` : undefined,
operationLogs: operationLogs
})
}
return mockOrders
}
// 计算属性
const uniqueBusinessNames = computed(() => {
return Array.from(new Set(orders.value.map(order => order.businessName))).sort()
})
const filteredOrders = computed(() => {
return orders.value.filter(order => {
// 搜索关键词匹配
const matchSearch = !searchKeyword.value ||
order.id.includes(searchKeyword.value) ||
order.crmOrderNumber?.includes(searchKeyword.value) ||
order.registerPersonPhone.includes(searchKeyword.value) ||
order.customerPhone.includes(searchKeyword.value)
// 业务状态匹配
const matchBusinessStatus = businessStatusFilter.value === 'all' || order.businessStatus === businessStatusFilter.value
// 审核状态匹配
const matchReviewStatus = reviewStatusFilter.value === 'all' || order.reviewStatus === reviewStatusFilter.value
// 业务名称匹配
const matchBusinessName = businessNameFilter.value === 'all' || order.businessName === businessNameFilter.value
// 日期范围匹配
let matchDate = true
if (dateRange.value && dateRange.value.length === 2) {
const orderDate = new Date(order.registerTime.split(' ')[0])
const startDate = new Date(dateRange.value[0])
const endDate = new Date(dateRange.value[1])
matchDate = orderDate >= startDate && orderDate <= endDate
}
return matchSearch && matchBusinessStatus && matchReviewStatus && matchBusinessName && matchDate
})
})
const totalPages = computed(() => {
return Math.ceil(filteredOrders.value.length / pageSize.value)
})
const paginatedOrders = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return filteredOrders.value.slice(start, end)
})
const uniqueBusinessNames = ref<any[]>([])
const totalPages = ref('')
const totalElements = ref('')
const paginatedOrders = ref<any[]>([])
// 状态样式映射 - 使用浅色背景样式
const getBusinessStatusClass = (status: BusinessStatus) => {
const statusMap = {
const statusMap: Record<string, string> = {
'-1': 'bg-blue-100 text-blue-800',
'1': 'bg-green-100 text-green-800',
'0': 'bg-gray-100 text-gray-800',
'待办理': 'bg-blue-100 text-blue-800',
'办理成功': 'bg-green-100 text-green-800',
'已关闭': 'bg-gray-100 text-gray-800'
}
return statusMap[status] || 'bg-gray-100 text-gray-800'
}
const getBusinessStatusName = (status: BusinessStatus) => {
const statusMap: Record<string, string> = {
'-1': '待办理',
'1': '办理成功',
'0': '已关闭',
'待办理': '待办理',
'办理成功': '办理成功',
'已关闭': '已关闭'
}
return statusMap[status] || ''
}
const getReviewStatusClass = (status: ReviewStatus) => {
const statusMap = {
const statusMap: Record<string, string> = {
'0': 'bg-purple-100 text-purple-800',
'1': 'bg-green-100 text-green-800',
'2': 'bg-orange-100 text-orange-800',
'待审核': 'bg-purple-100 text-purple-800',
'审核通过': 'bg-green-100 text-green-800',
'审核驳回': 'bg-orange-100 text-orange-800'
}
return statusMap[status] || 'bg-gray-100 text-gray-800'
}
const getReviewStatusName = (status: ReviewStatus) => {
const statusMap: Record<string, string> = {
'0': '待审核',
'1': '审核通过',
'2': '审核驳回',
'待审核': '待审核',
'审核通过': '审核通过',
'审核驳回': '审核驳回'
}
return statusMap[status] || '--'
}
// 判断行是否可选择(只有待审核的订单才能被选择)
const isRowSelectable = (row: any) => {
return row.reviewStatus === '待审核'
return row.auditStatus === 0
}
// 事件处理
const handleSearch = () => {
currentPage.value = 1
ElMessage.success('查询完成')
// 清空选中状态,防止按钮禁用问题
selectedOrderIds.value = []
queryOrder()
}
const handleReset = () => {
searchKeyword.value = ''
customerPhone.value = ''
chinaPersonPhone.value = ''
dateRange.value = null
businessStatusFilter.value = 'all'
reviewStatusFilter.value = 'all'
businessNameFilter.value = 'all'
businessStatusFilter.value = ''
reviewStatusFilter.value = ''
businessNameFilter.value = ''
currentPage.value = 1
ElMessage.success('已重置筛选条件')
}
......@@ -878,8 +714,43 @@ const handleSelectionChange = (selection: any[]) => {
selectedOrderIds.value = selection.map(item => item.id)
}
const handleExport = () => {
ElMessage.success('导出功能开发中')
const handleExport = async () => {
let start = ''
let end = ''
if(dateRange.value){
start = new Date(dateRange.value[0]).getTime()+''
end = (new Date(dateRange.value[1]).getTime()+24*60*60*1000-1)+''
}
const res = await $api.exportOrderList({
customerPhone: customerPhone.value,
chinaPersonPhone: chinaPersonPhone.value,
startTime: start,
endTime: end,
status: businessStatusFilter.value,
auditStatus: reviewStatusFilter.value,
jobId: businessNameFilter.value,
page: currentPage.value,
pageSize: pageSize.value
})
if(res.type == 'blob') {
let fileJson = {
content: res.value,
fileType: 'xlsx',
fileName: '登记订单数据'
}
$utils.exportExcel(fileJson)
} else {
let errorText
try {
errorText = res.value.msg
} catch(e) {
errorText = '网络异常,请稍后重试'
}
ElMessage.error(errorText)
}
}
const handleBatchReview = () => {
......@@ -887,9 +758,10 @@ const handleBatchReview = () => {
ElMessage.error('请选择要审核的订单')
return
}
// 确保只对待审核的订单进行批量审核
const pendingOrders = orders.value.filter(order =>
selectedOrderIds.value.includes(order.id) && order.reviewStatus === '待审核'
const pendingOrders = paginatedOrders.value.filter(order =>
selectedOrderIds.value.includes(order.id) && order.auditStatus === 0
)
if (pendingOrders.length === 0) {
ElMessage.error('所选订单中没有待审核的订单')
......@@ -906,7 +778,12 @@ const handleBatchModify = () => {
}
const handleViewDetail = (order: any) => {
emit('view-order-detail', order)
let param = {...order}
param.status += ''
param.auditStatus += ''
emit('view-order-detail', param)
}
const handleApprove = (order: any) => {
......@@ -914,32 +791,33 @@ const handleApprove = (order: any) => {
isReviewDialogOpen.value = true
}
const handleConfirmApprove = () => {
const handleConfirmApprove = async () => {
if (reviewingOrder.value) {
const currentTime = new Date().toLocaleString('zh-CN')
const order = orders.value.find(o => o.id === reviewingOrder.value.id)
const order = paginatedOrders.value.find(o => o.id === reviewingOrder.value.id)
if (order) {
// 更新订单状态
order.reviewStatus = '审核通过'
order.paymentTime = currentTime
// 添加操作日志
if (!order.operationLogs) {
order.operationLogs = []
try {
const response = await $api.audioOrderList({
list:[{
id: order.id,
auditStatus: '1'
}]
})
if (response.c === 0) {
ElMessage.success(`订单 ${reviewingOrder.value.id} 审核通过`)
isReviewDialogOpen.value = false
setTimeout(()=>{
reviewingOrder.value = null
},0)
queryOrder()
} else {
ElMessage.error(response.m)
}
} catch (error) {
}
order.operationLogs.push({
id: `LOG${order.id}_${Date.now()}`,
time: currentTime,
operator: '主管理员',
action: '审核通过',
details: `主管审核通过,金额:¥${order.actualReward?.toFixed(2) || order.estimatedReward.toFixed(2)}`
})
emit('orders-update', orders.value)
}
ElMessage.success(`订单 ${reviewingOrder.value.id} 审核通过`)
isReviewDialogOpen.value = false
reviewingOrder.value = null
}
}
......@@ -948,146 +826,142 @@ const handleReject = () => {
isRejectDialogOpen.value = true
}
const handleConfirmReject = () => {
const handleConfirmReject = async() => {
if (!rejectReason.value.trim()) {
ElMessage.error('请填写驳回原因')
return
}
if (reviewingOrder.value) {
const currentTime = new Date().toLocaleString('zh-CN')
const order = orders.value.find(o => o.id === reviewingOrder.value.id)
const order = paginatedOrders.value.find(o => o.id === reviewingOrder.value.id)
if (order) {
// 更新订单状态
order.reviewStatus = '审核驳回'
order.rejectReason = rejectReason.value
// 添加操作日志
if (!order.operationLogs) {
order.operationLogs = []
try {
const response = await $api.audioOrderList({
list:[{
id: order.id,
auditStatus: '2',
auditMemo: rejectReason.value
}]
})
if (response.c === 0) {
ElMessage.success(`订单 ${reviewingOrder.value.id} 已驳回`)
isReviewDialogOpen.value = false
isRejectDialogOpen.value = false
setTimeout(()=>{
reviewingOrder.value = null
rejectReason.value = ''
},0)
queryOrder()
} else {
ElMessage.error(response.m)
}
} catch (error) {
}
order.operationLogs.push({
id: `LOG${order.id}_${Date.now()}`,
time: currentTime,
operator: '主管理员',
action: '审核驳回',
details: `驳回原因:${rejectReason.value}`
})
emit('orders-update', orders.value)
}
ElMessage.success(`订单 ${reviewingOrder.value.id} 已驳回`)
isReviewDialogOpen.value = false
isRejectDialogOpen.value = false
reviewingOrder.value = null
rejectReason.value = ''
}
}
const handleConfirmBatchReject = () => {
const handleConfirmBatchReject = async () => {
if (batchReviewOrders.value.length === 0) {
ElMessage.error('没有待审核的订单')
return
}
const currentTime = new Date().toLocaleString('zh-CN')
let rejectCount = 0
let list: any[] = []
batchReviewOrders.value.forEach(order => {
const orderInList = orders.value.find(o => o.id === order.id)
if (orderInList && orderInList.reviewStatus === '待审核') {
orderInList.reviewStatus = '审核驳回'
orderInList.rejectReason = '批量审核驳回'
if (!orderInList.operationLogs) {
orderInList.operationLogs = []
}
orderInList.operationLogs.push({
id: `LOG${orderInList.id}_${Date.now()}_${Math.random()}`,
time: currentTime,
operator: '主管理员',
action: '审核驳回',
details: '批量审核驳回:未通过主管复核'
})
rejectCount++
}
list.push({
id: order.id,
auditStatus: '2',
auditMemo: batchRejectReason.value
})
})
selectedOrderIds.value = []
batchReviewOrders.value = []
isBatchReviewDialogOpen.value = false
batchRejectReason.value = ''
showBatchRejectReason.value = false
emit('orders-update', orders.value)
ElMessage.success(`成功批量驳回 ${rejectCount} 个订单`)
const response = await $api.audioOrderList({
list: list
})
if (response.c === 0) {
ElMessage.success(`成功批量驳回 ${batchReviewOrders.value.length} 个订单`)
isBatchReviewDialogOpen.value = false
showBatchRejectReason.value = false
setTimeout(() => {
selectedOrderIds.value = []
batchReviewOrders.value = []
batchRejectReason.value = ''
}, 0);
queryOrder()
} else {
ElMessage.error(response.m)
}
}
const handleConfirmBatchApprove = () => {
const handleConfirmBatchApprove = async () => {
if (batchReviewOrders.value.length === 0) {
ElMessage.error('没有待审核的订单')
return
}
const currentTime = new Date().toLocaleString('zh-CN')
let successCount = 0
let list: any[] = []
batchReviewOrders.value.forEach(order => {
const orderInList = orders.value.find(o => o.id === order.id)
if (orderInList && orderInList.reviewStatus === '待审核') {
// 更新订单状态
orderInList.reviewStatus = '审核通过'
orderInList.paymentTime = currentTime
// 添加操作日志
if (!orderInList.operationLogs) {
orderInList.operationLogs = []
}
orderInList.operationLogs.push({
id: `LOG${orderInList.id}_${Date.now()}_${Math.random()}`,
time: currentTime,
operator: '主管理员',
action: '审核通过',
details: `主管批量审核通过,金额:¥${orderInList.actualReward?.toFixed(2) || orderInList.estimatedReward.toFixed(2)}`
})
successCount++
}
list.push({
id: order.id,
auditStatus: '1'
})
})
const response = await $api.audioOrderList({
list: list
})
// 清空选择
selectedOrderIds.value = []
batchReviewOrders.value = []
isBatchReviewDialogOpen.value = false
emit('orders-update', orders.value)
ElMessage.success(`成功批量审核通过 ${successCount} 个订单`)
if (response.c === 0) {
ElMessage.success(`成功批量审核通过 ${batchReviewOrders.value.length} 个订单`)
isBatchReviewDialogOpen.value = false
setTimeout(() => {
selectedOrderIds.value = []
batchReviewOrders.value = []
}, 0);
queryOrder()
} else {
ElMessage.error(response.m)
}
}
const handlePageSizeChange = () => {
currentPage.value = 1
queryOrder()
}
const handleCurrentChange = (page: number) => {
currentPage.value = page
queryOrder()
}
// 批量修改:模板下载
const downloadBatchTemplate = () => {
const header = ['订单ID', '当前酬金', '新酬金', '备注']
const sample = [
['ORD20251107XXX', '50.00', '55.00', '调整酬金'],
['ORD20251107YYY', '30.00', '35.00', '补贴']
]
const lines = [header, ...sample].map(cols => cols.join(',')).join('\n')
const blob = new Blob([lines], { type: 'text/csv;charset=utf-8;' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = '批量修改模板.csv'
a.click()
URL.revokeObjectURL(url)
try {
// 创建下载链接
const link = document.createElement('a')
// 使用相对路径,确保本地和线上都能正常访问
link.href = './static/file/order_tem.xlsx'
link.download = '批量修改酬金模板.xlsx'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
ElMessage.success('模板下载成功')
} catch (error) {
console.error('下载模板失败:', error)
ElMessage.error('模板下载失败,请稍后重试')
}
}
// 批量修改:重置状态
......@@ -1121,71 +995,18 @@ const parseCsv = (text: string): string[][] => {
}
// 校验并映射为预览行
const buildPreviewRows = (rows: string[][]) => {
const header = rows[0] || []
const body = rows.slice(1)
const getIndex = (name: string) => header.findIndex(h => h.replace(/\s/g, '') === name)
const idxOrderId = getIndex('订单ID')
const idxCurrent = getIndex('当前酬金')
const idxNew = getIndex('新酬金')
const idxRemark = getIndex('备注')
const buildPreviewRows = (list:any[]) => {
const preview: any[] = []
const valid: any[] = []
const invalid: any[] = []
if (idxOrderId === -1 || idxNew === -1) {
ElMessage.error('模板列缺失,请使用系统模板:需要包含 订单ID、新酬金')
return { preview, valid, invalid }
}
const maxCount = 100
if (body.length > maxCount) {
ElMessage.warning(`仅解析前 ${maxCount} 条记录,其余将忽略`)
}
const limitRows = body.slice(0, maxCount)
limitRows.forEach((cols, i) => {
const orderId = cols[idxOrderId] || ''
const newRewardStr = cols[idxNew] || ''
const remark = idxRemark >= 0 ? (cols[idxRemark] || '') : ''
const order = orders.value.find(o => o.id === orderId)
const currentReward = order?.actualReward ?? order?.estimatedReward
list.forEach(item=>{
preview.push(item)
let status = 'OK'
let error = ''
if (!orderId) {
status = 'ERROR'
error = '订单ID为空'
} else if (!order) {
status = 'ERROR'
error = '订单不存在'
}
const newReward = Number(newRewardStr)
if (Number.isNaN(newReward)) {
status = 'ERROR'
error = error ? `${error}; 新酬金非数字` : '新酬金非数字'
} else if (newReward < 0 || newReward > 1000) {
status = 'ERROR'
error = error ? `${error}; 新酬金需在0~1000之间` : '新酬金需在0~1000之间'
}
const row = {
orderId,
currentReward: currentReward ?? '',
newReward: Number.isNaN(newReward) ? '' : newReward,
remark,
status,
error
}
preview.push(row)
if (status === 'OK') {
valid.push(row)
} else {
invalid.push(row)
if(item.memo){
invalid.push(item)
}else{
valid.push(item)
}
})
......@@ -1193,36 +1014,72 @@ const buildPreviewRows = (rows: string[][]) => {
}
// 上传变更
const handleBatchFileChange = (file: any) => {
const handleBatchFileChange = async (file: any) => {
if (!file?.raw) return
const raw: File = file.raw
batchFileName.value = raw.name
const reader = new FileReader()
reader.onload = () => {
const text = String(reader.result || '')
const rows = parseCsv(text)
const { preview, valid, invalid } = buildPreviewRows(rows)
batchPreviewRows.value = preview
batchValidRows.value = valid
batchInvalidRows.value = invalid
batchParseDone.value = true
if (valid.length === 0) {
ElMessage.warning('未检测到有效记录,请检查文件内容')
}
// 验证文件大小(2MB)
const maxSize = 2 * 1024 * 1024
if (raw.size > maxSize) {
ElMessage.error('文件大小超过2MB限制,请压缩后重新上传')
return
}
// 验证文件格式
const fileName = raw.name.toLowerCase()
if (!fileName.endsWith('.csv') && !fileName.endsWith('.xlsx')) {
ElMessage.error('目前仅支持 csv和xlsx 格式的文件')
return
}
reader.onerror = () => {
batchFileName.value = raw.name
try {
const fd = new FormData()
fd.append('file', raw)
const response = await $api.updateOrderListMoney(fd)
if(response.c === 0){
const { preview, valid, invalid } = buildPreviewRows(response.d.list)
batchPreviewRows.value = preview
batchValidRows.value = valid
batchInvalidRows.value = invalid
batchParseDone.value = true
if (valid.length === 0) {
ElMessage.warning('未检测到有效记录,请检查文件内容')
}
}else{
ElMessage.error(response.m)
}
} catch (error) {
ElMessage.error('读取文件失败,请重试')
}
reader.readAsText(raw, 'utf-8')
}
const closeBatchDialog = () => {
isBatchModifyDialogOpen.value = false
}
const openVerifyDialog = () => {
isVerifyDialogOpen.value = true
const openVerifyDialog = async () => {
const response = await $api.updateOrderMoney({
list: batchPreviewRows.value
})
if(response.c === 0){
ElMessage.success(`成功修改 ${batchPreviewRows.value.length} 个订单`)
isVerifyDialogOpen.value = false
isBatchModifyDialogOpen.value = false
setTimeout(()=>{
resetBatchState()
},0)
queryOrder()
}else{
ElMessage.error(response.m)
}
}
// 执行变更(演示校验码:123456)
......@@ -1256,14 +1113,56 @@ const applyBatchModify = () => {
// 初始化数据
onMounted(() => {
if (props.orders.length === 0) {
const newOrders = generateMockOrders()
orders.value = newOrders
emit('orders-update', newOrders)
} else {
orders.value = props.orders
}
queryOrder()
queryRule()
})
const queryRule = async ()=>{
try {
const response = await $api.queryAllRewardList({})
if (response.c === 0 && response.d) {
uniqueBusinessNames.value = response.d
} else {
}
} catch (error) {
}
}
const queryOrder = async ()=>{
try {
let start = ''
let end = ''
if(dateRange.value){
start = new Date(dateRange.value[0]).getTime()+''
end = (new Date(dateRange.value[1]).getTime()+24*60*60*1000-1)+''
}
const response = await $api.queryOrderList({
customerPhone: customerPhone.value,
chinaPersonPhone: chinaPersonPhone.value,
startTime: start,
endTime: end,
status: businessStatusFilter.value,
auditStatus: reviewStatusFilter.value,
jobId: businessNameFilter.value,
page: currentPage.value,
pageSize: pageSize.value
})
if(response.c === 0){
paginatedOrders.value = response.d.content
totalElements.value = response.d.totalElements
totalPages.value = response.d.totalPages
}else{
ElMessage.error(response.m)
}
} catch (error) {
}
}
// 监听props变化
watch(() => props.orders, (newOrders) => {
......
......@@ -48,7 +48,6 @@
class="inline-block w-2 h-2 rounded-sm bg-white"
/>
</div>
<!-- 组织图标 -->
<Building2
:class="[
......@@ -110,11 +109,9 @@
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { Building2, ChevronDown, ChevronRight } from 'lucide-vue-next'
interface Organization {
id: string
name: string
......@@ -122,35 +119,30 @@ interface Organization {
parentId?: string
children?: Organization[]
}
interface Role {
id: string
name: string
level: '地市级' | '区县级' | '一线人员'
}
interface Props {
organizations: Organization[]
selectedId?: string
roleId?: string
roles?: Role[]
expandedIds?: Set<string>
accountType?: '地市级' | '区县级' | '一线人员'
accountType?: '1' | '2' | '3'
}
const props = withDefaults(defineProps<Props>(), {
selectedId: '',
roleId: '',
roles: () => [],
expandedIds: () => new Set(),
accountType: ''
accountType: undefined
})
const emit = defineEmits<{
select: [orgId: string]
'toggle-expand': [orgId: string]
}>()
// 如果没有传入 expandedIds,使用本地状态
const localExpandedIds = ref(new Set<string>())
const expandedIds = computed(() =>
......@@ -158,36 +150,28 @@ const expandedIds = computed(() =>
? props.expandedIds
: localExpandedIds.value
)
// 工具函数
const hasChildren = (org: Organization): boolean => {
return !!(org.children && org.children.length > 0)
}
const isSelectable = (org: Organization): boolean => {
console.log('检查组织可选性:', org.name, org.type, '账号类型:', props.accountType)
//console.log('检查组织可选性:', org.name, org.type, '账号类型:', props.accountType)
// 只根据账号类型判断,不根据角色层级判断
if (props.accountType) {
switch (props.accountType) {
case '地市级':
case '1':
// 地市级只能选择地市级组织
console.log('地市级账号,检查组织类型是否为地市:', org.type === '地市')
//console.log('地市级账号,检查组织类型是否为地市:', org.type === '地市')
return org.type === '地市'
case '区县级':
case '2':
// 区县级只能选择区县级组织
console.log('区县级账号,检查组织类型是否为区县:', org.type === '区县')
//console.log('区县级账号,检查组织类型是否为区县:', org.type === '区县')
return org.type === '区县'
case '3':
// 区县级只能选择区县级组织
//console.log('区县级账号,检查组织类型是否为区县:', org.type === '区县')
return org.type === '区县'
case '一线人员':
// 一线人员只能选择区县下的客户经理团队
console.log('一线人员账号,检查组织类型是否为客户经理团队:', org.type === '客户经理团队')
if (org.type !== '客户经理团队') {
return false
}
// 检查该客户经理团队是否属于某个区县
const result = isUnderCounty(org)
console.log('客户经理团队是否属于区县下:', result)
return result
default:
return true
}
......@@ -196,7 +180,6 @@ const isSelectable = (org: Organization): boolean => {
// 如果没有账号类型,可以选择所有组织
return true
}
// 检查组织是否属于区县下
const isUnderCounty = (org: Organization): boolean => {
console.log('OrganizationTree.vue:202 检查组织是否属于区县下:', org.name, org.type, org.id)
......@@ -239,7 +222,6 @@ const isUnderCounty = (org: Organization): boolean => {
console.log('OrganizationTree.vue:241 未找到区县父组织')
return false
}
const getOrgTypeLabel = (type: Organization['type']) => {
switch (type) {
case '地市':
......@@ -252,21 +234,18 @@ const getOrgTypeLabel = (type: Organization['type']) => {
return type
}
}
const getRoleLevel = (roleId: string): string => {
if (!props.roles || props.roles.length === 0) return '区县级'
const role = props.roles.find(r => r.id === roleId)
return role ? role.level : '区县级'
}
// 事件处理
const handleSelect = (org: Organization) => {
if (isSelectable(org)) {
emit('select', org.id)
}
}
const toggleExpand = (orgId: string) => {
if (props.expandedIds && props.expandedIds.size > 0) {
// 使用父组件的展开状态
......@@ -280,11 +259,9 @@ const toggleExpand = (orgId: string) => {
}
}
}
const handleToggleExpand = (orgId: string) => {
emit('toggle-expand', orgId)
}
// 初始化展开顶级组织
if (props.expandedIds && props.expandedIds.size === 0) {
props.organizations.forEach(org => {
......@@ -293,129 +270,101 @@ if (props.expandedIds && props.expandedIds.size === 0) {
}
})
}
</script>
</script>
<style scoped>
.space-y-1 > * + * {
margin-top: 0.25rem;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.gap-2 {
gap: 0.5rem;
}
.p-1 {
padding: 0.25rem;
}
.p-2 {
padding: 0.5rem;
}
.rounded {
border-radius: 0.25rem;
}
.cursor-pointer {
cursor: pointer;
}
.cursor-not-allowed {
cursor: not-allowed;
}
.transition-colors {
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.opacity-40 {
opacity: 0.4;
}
.bg-blue-50 {
background-color: #eff6ff;
}
.border {
border-width: 1px;
}
.border-blue-500 {
border-color: #3b82f6;
}
.hover\:bg-neutral-50:hover {
background-color: #f9fafb;
}
.hover\:bg-neutral-200:hover {
background-color: #e5e7eb;
}
.h-4 {
height: 1rem;
}
.w-4 {
width: 1rem;
}
.w-6 {
width: 1.5rem;
}
.text-neutral-300 {
color: #d1d5db;
}
.text-neutral-400 {
color: #9ca3af;
}
.text-neutral-500 {
color: #6b7280;
}
.text-neutral-600 {
color: #4b5563;
}
.text-neutral-900 {
color: #111827;
}
.text-blue-600 {
color: #2563eb;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.flex-1 {
flex: 1 1 0%;
}
.ml-4 {
margin-left: 1rem;
}
.border-l-2 {
border-left-width: 2px;
}
.border-neutral-200 {
border-color: #e5e7eb;
}
</style>
</style>
\ No newline at end of file
<template>
<div class="space-y-1">
<div
:class="`permission-tree-node flex items-center gap-2 p-2 rounded ${level > 0 ? 'ml-6' : ''}`"
>
:class="`permission-tree-node flex items-center gap-2 p-2 rounded ${level > 0 ? 'ml-6' : ''}`">
<!-- 展开/收起按钮 -->
<button
v-if="hasChildren"
......@@ -27,18 +26,18 @@
<div class="flex-1">
<div class="flex items-center gap-2">
<span class="text-neutral-900">{{ permission.name }}</span>
<el-tag
<!-- <el-tag
type="info"
effect="plain"
size="small"
class="text-xs"
>
{{ permission.code }}
</el-tag>
</el-tag> -->
</div>
<p v-if="permission.description" class="text-xs text-neutral-500 mt-1">
<!-- <p v-if="permission.description" class="text-xs text-neutral-500 mt-1">
{{ permission.description }}
</p>
</p> -->
</div>
</div>
......@@ -103,9 +102,29 @@ const isSelected = computed(() =>
const isIndeterminate = computed(() => {
if (isSelected.value || !hasChildren.value) return false
return props.permission.children!.some(c =>
props.selectedPermissions.includes(c.id)
)
// 获取所有子权限ID
const getAllChildIds = (children: Permission[]): string[] => {
const ids: string[] = []
for (const child of children) {
ids.push(child.id)
if (child.children) {
ids.push(...getAllChildIds(child.children))
}
}
return ids
}
const allChildIds = getAllChildIds(props.permission.children!)
if (allChildIds.length === 0) return false
// 检查是否所有子权限都被选中
const allSelected = allChildIds.every(id => props.selectedPermissions.includes(id))
if (allSelected) return false
// 检查是否有部分子权限被选中
const someSelected = allChildIds.some(id => props.selectedPermissions.includes(id))
return someSelected
})
</script>
......
......@@ -23,44 +23,28 @@
:data="filteredRoles"
style="width: 100%"
:header-cell-style="{ backgroundColor: '#f3f4f6', color: '#374151', fontWeight: '500', borderBottom: '1px solid #e5e7eb' }"
:row-style="{ borderBottom: '1px solid rgb(243 244 246)' }"
>
<el-table-column prop="name" label="角色名称" min-width="120">
:row-style="{ borderBottom: '1px solid rgb(243 244 246)' }">
<el-table-column prop="roleName" label="角色名称" min-width="120">
<template #default="{ row }">
<span class="text-neutral-900">{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column prop="permissionIds" label="权限数量" min-width="100">
<template #default="{ row }">
<el-tag
type="info"
effect="plain"
size="small"
class="bg-neutral-50"
>
{{ row.permissionIds.length }} 个权限
</el-tag>
<span class="text-neutral-900">{{ row.roleName }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" min-width="80">
<template #default="{ row }">
<el-tag
:type="row.status === '启用' ? 'success' : 'info'"
:type="row.status == 1 ? 'success' : 'info'"
effect="plain"
size="small"
>
{{ row.status }}
{{ row.status==1?'启用':'停用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="150">
<template #default="{ row }">
<span class="text-neutral-600">{{ row.createTime || '-' }}</span>
<span class="text-neutral-600">{{ $utils.detailTime(row.createTime)}}</span>
</template>
</el-table-column>
......@@ -132,6 +116,8 @@
</div>
<el-switch
v-model="roleStatusEnabled"
active-text="开启"
inactive-text="关闭"
/>
</div>
</div>
......@@ -142,13 +128,13 @@
<label class="block text-sm font-medium text-neutral-900">
权限选择 <span class="text-red-500">*</span>
</label>
<el-tag
<!-- <el-tag
type="primary"
effect="plain"
class="bg-brand-primary/10 text-brand-primary border-brand-primary"
>
已选择 {{ selectedPermissions.length }} 个权限
</el-tag>
</el-tag> -->
</div>
<div class="border border-neutral-300 rounded p-4 bg-neutral-50 max-h-96 overflow-y-auto">
......@@ -191,11 +177,32 @@
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ref,onMounted, computed, watch ,getCurrentInstance} from 'vue'
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import PermissionTreeNode from './PermissionTreeNode.vue'
const { $api,$utils } = getCurrentInstance()!.appContext.config.globalProperties
onMounted(() => {
// 初始化数据
handleFilter()
})
const handleFilter = async () => {
try {
const response = await $api.queryRoleList({});
if (response.c === 0) {
filteredRoles.value = response.d
ElMessage.success('查询成功');
} else {
ElMessage.error(response.msg || '查询失败');
}
} catch (error: any) {
ElMessage.error('查询失败: ' + error.message);
}
}
// 类型定义
export interface Permission {
id: string
......@@ -207,17 +214,18 @@ export interface Permission {
}
export interface Role {
id: string
id: Number
name: string
description?: string
level: RoleLevel
permissionIds: string[]
roleName?: string
level: '地市级' | '区县级' | '一线人员'
status: RoleStatus
remark?: string
permissionIds?: string[]
permissions?: string[]
createTime?: string
}
export type RoleLevel = '地市级' | '区县级' | '一线人员'
export type RoleStatus = '启用' | '禁用'
export type RoleStatus = 1 | 0
// Props
interface Props {
......@@ -242,50 +250,101 @@ const roleName = ref('')
const roleDescription = ref('')
const selectedPermissions = ref<string[]>([])
const roleLevel = ref<'地市级' | '区县级' | '一线人员'>('区县级')
const roleStatusEnabled = ref(true)
// 权限树展开状态
const expandedPermissions = ref<Set<string>>(new Set())
// 计算属性
const roleStatus = computed((): RoleStatus => roleStatusEnabled.value ? '启用' : '禁用')
const roleStatus = computed((): RoleStatus => roleStatusEnabled.value ? 1 : 0)
const topLevelPermissions = computed(() =>
props.permissions.filter(p => !p.parentId)
)
const filteredRoles = computed(() =>
props.roles.filter(role => role.name !== '地市主管理员')
)
const filteredRoles = ref([])
const getAllIds = (tree: any[]) => {
const ids = [] // 结果池
const stack = [...tree] // 根节点入栈
while (stack.length) {
const node = stack.pop()
if (node.id !== undefined) ids.push(node.id) // 收集当前节点
if (node.children?.length) {
// 子节点全部入栈(顺序无所谓就 push,要顺序就 unshift)
stack.push(...node.children)
}
}
return ids
}
// 获取角色的权限ID列表,兼容不同的字段名
const getRolePermissionIds = async (role: Role)=> {
try {
const response = await $api.queryRolePermission({
id: role.id
})
if (response.c === 0 && response.d) {
return getAllIds(response.d.list)
} else {
console.warn('获取角色权限失败:', response.msg)
return role.permissionIds || role.permissions || []
}
} catch (error) {
console.error('获取角色权限出错:', error)
// 如果API调用失败,使用角色对象中已有的权限数据
return role.permissionIds || role.permissions || []
}
}
// 打开新增对话框
const handleOpenAddDialog = () => {
editingRole.value = null
roleName.value = ''
roleDescription.value = ''
roleStatusEnabled.value = true
selectedPermissions.value = []
roleStatusEnabled.value = true
expandedPermissions.value = new Set(props.permissions.filter(p => !p.parentId).map(p => p.id))
isDialogOpen.value = true
}
// 打开编辑对话框
const handleOpenEditDialog = (role: Role) => {
const handleOpenEditDialog = async (role: Role) => {
editingRole.value = role
roleName.value = role.name
roleDescription.value = role.description || ''
selectedPermissions.value = [...role.permissionIds]
roleStatusEnabled.value = role.status === '启用'
roleName.value = role.roleName || ''
roleDescription.value = role.remark || ''
roleStatusEnabled.value = role.status === 1
// 获取角色权限并设置选中状态
const permissionIds = await getRolePermissionIds(role)
selectedPermissions.value = permissionIds
// 展开顶级权限以便用户能看到选中的权限
expandedPermissions.value = new Set(props.permissions.filter(p => !p.parentId).map(p => p.id))
// 如果有选中的权限,也展开其父级权限
permissionIds.forEach(permissionId => {
const permission = findPermissionById(permissionId)
if (permission && permission.parentId) {
// 向上展开所有父级
let currentPermission = permission
while (currentPermission.parentId) {
expandedPermissions.value.add(currentPermission.parentId)
currentPermission = findPermissionById(currentPermission.parentId)!
}
}
})
isDialogOpen.value = true
}
// 保存角色
const handleSave = () => {
const handleSave = async () => {
if (!roleName.value.trim()) {
ElMessage.error('请输入角色名称')
return
......@@ -296,25 +355,41 @@ const handleSave = () => {
return
}
const roleData = {
name: roleName.value.trim(),
description: roleDescription.value.trim(),
permissionIds: selectedPermissions.value,
status: roleStatus.value
}
if (editingRole.value) {
emit('updateRole', editingRole.value.id, roleData)
ElMessage.success('角色更新成功')
const response = await $api.createOrUpdateRole({
roleId: editingRole.value?editingRole.value.id:'',
roleName: roleName.value.trim(),
remark: roleDescription.value.trim(),
status: roleStatus.value,
functionIds: selectedPermissions.value
})
if (response.c === 0) {
handleFilter()
ElMessage.success(editingRole.value?'编辑成功':'创建成功');
} else {
emit('addRole', roleData)
ElMessage.success('角色创建成功')
ElMessage.error(response.m);
}
isDialogOpen.value = false
}
// 获取所有子权限ID
const getAllChildPermissionIds = (permissionId: string): string[] => {
const permission = findPermissionById(permissionId)
if (!permission || !permission.children) return []
const childIds: string[] = []
const collectChildIds = (perms: Permission[]) => {
for (const p of perms) {
childIds.push(p.id)
if (p.children) {
collectChildIds(p.children)
}
}
}
collectChildIds(permission.children)
return childIds
}
// 切换权限选择
const handleTogglePermission = (permissionId: string) => {
const permission = findPermissionById(permissionId)
......@@ -330,8 +405,18 @@ const handleTogglePermission = (permissionId: string) => {
})
newSelected = newSelected.filter(id => id !== permissionId)
} else {
// 选择:自动选择所有父权限
// 选择:自动选择所有父权限和所有子权限
newSelected.push(permissionId)
// 自动选择所有子权限
const childIds = getAllChildPermissionIds(permissionId)
childIds.forEach(id => {
if (!newSelected.includes(id)) {
newSelected.push(id)
}
})
// 自动选择所有父权限
let current = permission
while (current.parentId) {
if (!newSelected.includes(current.parentId)) {
......@@ -342,6 +427,8 @@ const handleTogglePermission = (permissionId: string) => {
}
selectedPermissions.value = newSelected
console.log(newSelected)
}
// 查找权限
......
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
// import WelcomeItem from './WelcomeItem.vue' // 文件不存在,暂时注释
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
......
......@@ -15,65 +15,65 @@
新增用户
</el-button>
</div>
<!-- 搜索和筛选 -->
<el-card>
<div class="grid grid-cols-5 gap-4 px-6 py-6">
<div class="relative">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="generalSearch"
placeholder="搜索用户姓名/手机号/工号"
v-model="userNameSearch"
placeholder="搜索用户姓名"
class="h-10 search-input text-ellipsis-input"
clearable
/>
</div>
<el-select
v-model="roleFilter"
placeholder="角色"
class="h-10 w-full"
clearable
>
<el-option label="全部角色" value="all" />
<el-option
v-for="role in availableRoles"
:key="role.id"
:label="role.name"
:value="role.id"
<div class="relative">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="phoneSearch"
placeholder="搜索手机号"
class="h-10 search-input text-ellipsis-input"
clearable
/>
</el-select>
</div>
<div class="relative">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="codeSearch"
placeholder="搜索工号"
class="h-10 search-input text-ellipsis-input"
clearable
/>
</div>
<el-select
v-model="accountTypeFilter"
placeholder="账号类型"
class="h-10 w-full"
clearable
>
<el-option label="全部类型" value="all" />
<el-option label="地市级" value="地市级" />
<el-option label="区县级" value="区县级" />
<el-option label="一线人员" value="一线人员" />
<el-option label="全部类型" value="" />
<el-option label="地市级" value="1" />
<el-option label="区县级" value="2" />
<el-option label="一线人员" value="3" />
</el-select>
<el-select
v-model="statusFilter"
placeholder="状态"
class="h-10 w-full"
clearable
>
<el-option label="全部状态" value="all" />
<el-option label="正常" value="正常" />
<el-option label="禁用" value="禁用" />
<el-option label="全部状态" value="" />
<el-option label="开启" value="1" />
<el-option label="关闭" value="0" />
</el-select>
<el-select
v-model="organizationFilter"
placeholder="所属组织"
placeholder="所属区域"
class="h-10 w-full"
clearable
>
<el-option label="全部组织" value="all" />
<el-option label="全部区域" value="" />
<el-option
v-for="org in organizationOptions"
:key="org.id"
......@@ -81,9 +81,15 @@
:value="org.id"
/>
</el-select>
<el-button
class="h-13 px-2"
@click="getAccountList"
style="width: 100px;"
>
查询
</el-button>
</div>
</el-card>
<!-- 用户列表 -->
<el-card class="p-5">
......@@ -94,18 +100,10 @@
:header-cell-style="{ backgroundColor: '#f3f4f6', color: '#374151', fontWeight: '500', borderBottom: '1px solid #e5e7eb' }"
:row-style="getRowStyle"
>
<el-table-column prop="realName" label="用户姓名" min-width="100">
<el-table-column prop="username" label="用户姓名" min-width="100">
<template #default="{ row }">
<div class="flex items-center gap-2">
<span :class="{ 'font-semibold': isCurrentUser(row) }">{{ row.realName }}</span>
<el-tag
v-if="isCurrentUser(row)"
type="primary"
size="small"
class="text-xs"
>
当前登录
</el-tag>
<span>{{ row.username || '--' }}</span>
</div>
</template>
</el-table-column>
......@@ -116,54 +114,50 @@
</template>
</el-table-column>
<el-table-column prop="username" label="工号" min-width="120">
<el-table-column prop="userCode" label="工号" min-width="120">
<template #default="{ row }">
<span :class="{ 'font-semibold': isCurrentUser(row) }">{{ row.username }}</span>
<span>{{ row.userCode }}</span>
</template>
</el-table-column>
<el-table-column prop="accountType" label="账号类型" min-width="100">
<el-table-column prop="userType" label="账号类型" min-width="100">
<template #default="{ row }">
<el-tag
:type="getAccountTypeTagType(row.accountType)"
:type="getAccountTypeTagType(row.userType)"
effect="plain"
size="small"
>
{{ row.accountType || '-' }}
{{ getAccountTypeName(row.userType) || '-' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="roleId" label="角色" min-width="120">
<el-table-column prop="roleName" label="角色" min-width="120">
<template #default="{ row }">
{{ getRoleById(row.roleId)?.name || '-' }}
<span>{{ row.roleName || '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" min-width="80">
<template #default="{ row }">
<el-tag
:type="row.status === '正常' ? 'success' : 'info'"
:type="row.status === 1 ? 'success' : 'info'"
size="small"
>
{{ row.status }}
{{ row.status==1?'开启':'关闭' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="organizationId" label="所属组织" min-width="150">
<el-table-column prop="areaName" label="所属区域" min-width="150">
<template #default="{ row }">
<div v-if="getOrganizationById(row.organizationId)" class="flex items-center gap-2">
<el-tag size="small" type="info">{{ getOrganizationById(row.organizationId)?.type }}</el-tag>
<span>{{ getOrganizationById(row.organizationId)?.name }}</span>
</div>
<span v-else>-</span>
<span>{{ row.areaName || '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="120">
<template #default="{ row }">
{{ row.createTime || '-' }}
{{ $utils.detailTime(row.createTime) }}
</template>
</el-table-column>
......@@ -182,7 +176,6 @@
</el-table>
</div>
</el-card>
<!-- 新增/编辑用户对话框 -->
<el-dialog
v-model="isDialogOpen"
......@@ -205,23 +198,18 @@
:disabled="!!editingUser"
/>
</el-form-item>
<el-form-item label="用户姓名" required>
<el-input
v-model="formData.realName"
placeholder="真实姓名"
/>
</el-form-item>
<el-form-item label="手机号" required>
<el-input
v-model="formData.phone"
placeholder="11位手机号"
/>
</el-form-item>
<el-form-item label="账号类型" required>
<el-select
v-model="formData.accountType"
......@@ -229,12 +217,11 @@
class="w-full"
@change="handleAccountTypeChange"
>
<el-option label="地市级" value="地市级" />
<el-option label="区县级" value="区县级" />
<el-option label="一线人员" value="一线人员" />
<el-option label="地市级" value="1" />
<el-option label="区县级" value="2" />
<el-option label="一线人员" value="3" />
</el-select>
</el-form-item>
<el-form-item label="角色" required class="col-span-2">
<el-select
v-model="formData.roleId"
......@@ -245,7 +232,7 @@
<el-option
v-for="role in availableRoles"
:key="role.id"
:label="`${role.name}${role.description ? ` - ${role.description}` : ''}`"
:label="`${role.roleName}${role.remark ? ` - ${role.remark}` : ''}`"
:value="role.id"
/>
</el-select>
......@@ -254,7 +241,6 @@
</div>
</el-form-item>
</div>
<div class="flex items-center justify-between p-4 bg-neutral-50 rounded border border-neutral-200">
<div>
<div class="text-neutral-900 font-medium">用户状态</div>
......@@ -262,21 +248,22 @@
</div>
<el-switch
v-model="formData.isActive"
active-text="开启"
inactive-text="关闭"
/>
</div>
</div>
<!-- 所属组织(当前登录用户不可修改) -->
<div v-if="!editingUser || !isCurrentUser(editingUser)" class="space-y-4">
<div class="flex items-center justify-between pb-2 border-b border-neutral-200">
<h3 class="text-neutral-900">所属组织 <span class="text-red-500">*</span></h3>
<el-tag
<h3 class="text-neutral-900">所属区域 <span class="text-red-500">*</span></h3>
<!-- <el-tag
v-if="formData.organizationId"
type="primary"
size="small"
>
已选择
</el-tag>
</el-tag> -->
</div>
<div class="border border-neutral-300 rounded p-4 bg-neutral-50 max-h-[250px] overflow-y-auto">
......@@ -286,7 +273,7 @@
:role-id="formData.roleId"
:roles="roles"
:expanded-ids="organizationExpandedIds"
:account-type="formData.accountType"
:account-type="mapAccountType(formData.accountType)"
@select="handleOrganizationSelect"
@toggle-expand="handleToggleExpand"
/>
......@@ -296,7 +283,6 @@
提示:点击组织名称选择,点击箭头展开/收起子组织。灰色不可选的组织表示不符合当前角色的层级要求。
</p>
</div>
<!-- 当前登录用户的组织信息(只读显示) -->
<div v-else-if="editingUser && isCurrentUser(editingUser)" class="space-y-4">
<h3 class="text-neutral-900 pb-2 border-b border-neutral-200">所属组织</h3>
......@@ -320,37 +306,37 @@
</div>
</div>
</div>
<template #footer>
<el-button @click="isDialogOpen = false">取消</el-button>
<el-button type="primary" @click="handleSave">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ref, computed, watch ,onMounted,getCurrentInstance} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Building2, Search } from 'lucide-vue-next'
import OrganizationTree from './OrganizationTree.vue'
const { $api,$utils } = getCurrentInstance()!.appContext.config.globalProperties
// 类型定义
interface User {
export interface User {
id: string
username: string
realName: string
organizationId: string
roleId: string
accountType?: '地市级' | '区县级' | '一线人员'
accountType?: string
phone?: string
email?: string
status: '正常' | '禁用'
status: '1' | '0',
userType?: String
createTime?: string
creatorId?: string
creatorId?: string,
areaId?: String
}
interface Role {
id: string
name: string
......@@ -359,15 +345,14 @@ interface Role {
status: '启用' | '禁用'
permissions: string[]
}
interface Organization {
id: string
name: string
areaCode: string
type: '地市' | '区县' | '客户经理团队'
parentId?: string
children?: Organization[]
}
// Props
interface UserManagementProps {
users: User[]
......@@ -375,76 +360,77 @@ interface UserManagementProps {
organizations: Organization[]
currentUserId: string
}
const props = defineProps<UserManagementProps>()
const emit = defineEmits<{
addUser: [user: Omit<User, 'id' | 'createTime'>]
addUser: [user: Omit<User, 'id' | 'createTime' | 'creatorId'>]
updateUser: [id: string, updates: Partial<User>]
}>()
// 响应式数据
const isDialogOpen = ref(false)
const editingUser = ref<User | null>(null)
const generalSearch = ref('')
const userNameSearch = ref('')
const phoneSearch = ref('')
const codeSearch = ref('')
const roleFilter = ref<string>('all')
const accountTypeFilter = ref<'all' | '地市级' | '区县级' | '一线人员'>('all')
const statusFilter = ref<'all' | '正常' | '禁用'>('all')
const organizationFilter = ref<string>('all')
const accountTypeFilter = ref<string>('')
const statusFilter = ref<'' | '1' | '0'>('')
const organizationFilter = ref<string>('')
const organizationExpandedIds = ref(new Set<string>())
// 表单数据
const formData = ref({
username: '',
realName: '',
phone: '',
accountType: '',
accountType: '1',
organizationId: '',
roleId: '',
isActive: true
})
// 计算属性
const availableRoles = computed(() =>
props.roles.filter(r => r.status === '启用' && r.name !== '地市主管理员')
)
const availableRoles = ref<any[]>([])
const filteredUsers = ref<any[]>([])
const topLevelOrganizations = computed(() =>
props.organizations.filter(o => !o.parentId)
)
const totalUsers = computed(() => props.users.length - 1) // 排除当前登录用户
const hasFilters = computed(() =>
generalSearch.value !== '' ||
roleFilter.value !== 'all' ||
accountTypeFilter.value !== 'all' ||
statusFilter.value !== 'all' ||
organizationFilter.value !== 'all'
props.organizations.filter(o => o.id)
)
const filteredUsers = computed(() => {
return props.users.filter(user => {
// 过滤掉当前登录用户
if (user.id === props.currentUserId) return false
// 通用搜索:支持搜索用户姓名、手机号、工号
const searchTerm = generalSearch.value.toLowerCase()
let matchGeneral = true
if (searchTerm !== '') {
matchGeneral = user.realName.toLowerCase().includes(searchTerm) ||
(user.phone && user.phone.toLowerCase().includes(searchTerm)) ||
user.username.toLowerCase().includes(searchTerm)
onMounted(() => {
getRoleList()
getAccountList()
})
const getRoleList = async ()=>{
try {
const response = await $api.queryRoleList({})
if (response && response.c === 0) {
availableRoles.value = response.d.filter((item: any) => {return item.status==1})
}
} catch (error) {
}
}
const getAccountList = async ()=>{
try {
const response = await $api.queryAccountList({
username: userNameSearch.value,
phone: phoneSearch.value,
userCode: codeSearch.value,
userType: accountTypeFilter.value,
status: statusFilter.value,
areaId: organizationFilter.value,
page: 1,
pageSize: 1000
})
if (response && response.c === 0) {
filteredUsers.value = response.d.content
}
} catch (error) {
console.log('报错了')
}
const matchRole = roleFilter.value === 'all' || user.roleId === roleFilter.value
const matchAccountType = accountTypeFilter.value === 'all' || user.accountType === accountTypeFilter.value
const matchStatus = statusFilter.value === 'all' || user.status === statusFilter.value
const matchOrganization = organizationFilter.value === 'all' || user.organizationId === organizationFilter.value
}
return matchGeneral && matchRole && matchAccountType && matchStatus && matchOrganization
})
})
function mapAccountType(val: string | undefined): '1' | '2' | '3' | undefined {
if (val === '1' || val === '2' || val === '3') return val
return undefined // 非法值统一降级
}
const totalUsers = computed(() => props.users.length - 1) // 排除当前登录用户
const roleHint = computed(() => {
if (!formData.value.roleId) return ''
......@@ -454,7 +440,6 @@ const roleHint = computed(() => {
return `${role.name}`
})
// 工具函数
const getOrganizationById = (id: string): Organization | undefined => {
const find = (orgs: Organization[]): Organization | undefined => {
......@@ -469,85 +454,90 @@ const getOrganizationById = (id: string): Organization | undefined => {
}
return find(props.organizations)
}
const getRoleById = (id: string): Role | undefined => {
return props.roles.find(r => r.id === id)
}
const getAccountTypeTagType = (accountType: string) => {
const getAccountTypeTagType = (accountType: Number) => {
switch (accountType) {
case '地市级': return 'warning'
case '区县级': return 'primary'
case '一线人员': return 'success'
case 1: return 'warning'
case 2: return 'primary'
case 3: return 'success'
default: return 'info'
}
}
const getAccountTypeName = (accountType: Number) => {
switch (accountType) {
case 1: return '地市级'
case 2: return '区县级'
case 3: return '一线管理员'
default: return '--'
}
}
const isCurrentUser = (user: User): boolean => {
return user.id === props.currentUserId
}
const getRowStyle = ({ row }: { row: User }) => {
if (isCurrentUser(row)) {
return { backgroundColor: '#f0f9ff', borderLeft: '4px solid #1677ff' }
}
return {}
}
// 事件处理
const handleOpenAddDialog = () => {
editingUser.value = null
formData.value = {
username: '',
realName: '',
phone: '',
accountType: '',
accountType: '' as '1' | '2' | '3',
organizationId: '',
roleId: '',
isActive: true
}
isDialogOpen.value = true
}
const handleOpenEditDialog = (user: User) => {
editingUser.value = user
const handleOpenEditDialog = (row: any) => {
editingUser.value = row
console.log('编辑用户数据:', row)
formData.value = {
username: user.username,
realName: user.realName,
username: row.userCode,
realName: row.username,
phone: row.phone || '',
accountType: (row.userType + '') as '1' | '2' | '3',
organizationId: row.areaId + '',
roleId: row.roleId,
isActive: row.status === 1
}
console.log('设置的组织ID:', formData.value.organizationId, '类型:', typeof formData.value.organizationId)
console.log('组织数据:', props.organizations)
phone: user.phone || '',
accountType: user.accountType || '',
organizationId: user.organizationId,
roleId: user.roleId,
isActive: user.status === '正常'
// 调试:打印组织数据中的ID和类型
const debugOrgs = (orgs: any[], depth = 0) => {
orgs.forEach(org => {
console.log(`组织ID: ${org.id}, 类型: ${typeof org.id}, 名称: ${org.name}`)
if (org.children) debugOrgs(org.children, depth + 1)
})
}
debugOrgs(props.organizations)
// 自动展开到选中组织的路径
if (row.areaId) {
expandToOrganization(row.areaId + '')
}
isDialogOpen.value = true
}
const handleAccountTypeChange = () => {
// 当账号类型变更时,清空组织选择
formData.value.organizationId = ''
// 根据账号类型设置组织选择的层级限制
const accountType = formData.value.accountType
if (accountType === '地市级') {
ElMessage.info('账号类型为地市级,所属组织只能选择地市级')
} else if (accountType === '区县级') {
ElMessage.info('账号类型为区县级,所属组织只能选择区县级')
} else if (accountType === '一线人员') {
ElMessage.info('账号类型为一线人员,所属组织只能选择客户经理团队')
}
}
const handleRoleChange = () => {
// 角色变更时不进行层级检查
}
const handleOrganizationSelect = (orgId: string) => {
formData.value.organizationId = orgId
}
const handleToggleExpand = (orgId: string) => {
if (organizationExpandedIds.value.has(orgId)) {
organizationExpandedIds.value.delete(orgId)
......@@ -556,107 +546,108 @@ const handleToggleExpand = (orgId: string) => {
}
}
const isOrganizationSelectable = (orgType: string, roleId: string): boolean => {
// 只根据账号类型进行组织层级限制,不根据角色层级限制
const accountType = formData.value.accountType
switch (accountType) {
case '地市级':
// 地市级只能选择地市级组织
return orgType === '地市'
case '区县级':
// 区县级只能选择区县级组织
return orgType === '区县'
case '一线人员':
// 一线人员只能选择客户经理团队
return orgType === '客户经理团队'
default:
// 如果没有选择账号类型,可以选择所有组织
return true
// 展开到指定组织的路径
const expandToOrganization = (targetOrgId: string) => {
// 先清空展开状态
organizationExpandedIds.value.clear()
// 查找目标组织的所有父级组织ID
const findParentPath = (orgs: Organization[], targetId: string, path: string[] = []): string[] | null => {
for (const org of orgs) {
if (org.id == targetId) {
return [...path, org.id]
}
if (org.children) {
const result = findParentPath(org.children, targetId, [...path, org.id])
if (result) return result
}
}
return null
}
}
const handleSave = () => {
const path = findParentPath(props.organizations, targetOrgId)
console.log('展开路径:', path, '目标ID:', targetOrgId)
if (path) {
// 展开路径上的所有组织(除了目标组织本身)
path.slice(0, -1).forEach(orgId => {
organizationExpandedIds.value.add(orgId)
})
console.log('展开的组织ID:', Array.from(organizationExpandedIds.value))
} else {
console.warn('未找到目标组织:', targetOrgId)
}
}
const handleSave = async () => {
// 表单验证
if (!formData.value.username.trim()) {
ElMessage.error('请输入工号')
return
}
if (!formData.value.realName.trim()) {
ElMessage.error('请输入用户姓名')
return
}
if (!formData.value.phone.trim()) {
ElMessage.error('请输入手机号')
return
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(formData.value.phone.trim())) {
ElMessage.error('请输入正确的11位手机号')
return
}
// 当前登录用户不需要验证组织(不可修改)
if (!editingUser.value || !isCurrentUser(editingUser.value)) {
if (!formData.value.organizationId) {
ElMessage.error('请选择所属组织')
return
}
}
if (!formData.value.roleId) {
ElMessage.error('请选择角色')
return
}
if (!formData.value.accountType) {
ElMessage.error('请选择账号类型')
return
}
// 检查用户名是否重复
const existingUser = props.users.find(u =>
u.username === formData.value.username.trim() && u.id !== editingUser.value?.id
)
if (existingUser) {
ElMessage.error('该工号已存在')
if (!formData.value.roleId) {
ElMessage.error('请选择角色')
return
}
const userData: any = {
username: formData.value.username.trim(),
realName: formData.value.realName.trim(),
let data: any = {
id: editingUser.value?editingUser.value.id:'',
userCode: formData.value.username.trim(),
username: formData.value.realName.trim(),
phone: formData.value.phone.trim(),
userType: formData.value.accountType,
roleId: formData.value.roleId,
accountType: formData.value.accountType,
status: formData.value.isActive ? '正常' : '禁用',
phone: formData.value.phone.trim() || undefined,
creatorId: props.currentUserId
status: formData.value.isActive ? '1' : '0',
areaId: formData.value.organizationId
}
// 当前登录用户不可修改自己的所属组织
if (!editingUser.value || !isCurrentUser(editingUser.value)) {
userData.organizationId = formData.value.organizationId
if(editingUser.value){
if(editingUser.value.username == formData.value.realName.trim()){
delete data.username
}
if(editingUser.value.phone == formData.value.phone.trim()){
delete data.phone
}
if(editingUser.value.userType == formData.value.accountType){
delete data.userType
}
if(editingUser.value.roleId == formData.value.roleId){
delete data.roleId
}
if(editingUser.value.status == (formData.value.isActive ? '1' : '0')){
delete data.status
}
if(editingUser.value.areaId == formData.value.organizationId){
delete data.areaId
}
}
if (editingUser.value) {
emit('updateUser', editingUser.value.id, userData)
ElMessage.success('用户更新成功')
const response = await $api.addAndUpdateRole(data)
if (response.c === 0) {
getAccountList()
ElMessage.success(editingUser.value?'编辑成功':'创建成功');
} else {
emit('addUser', userData)
ElMessage.success('用户创建成功')
ElMessage.error(response.m);
}
isDialogOpen.value = false
}
// 组织下拉选项(扁平化带缩进)
const organizationOptions = computed(() => {
const result: { id: string; label: string }[] = []
......@@ -670,9 +661,7 @@ const organizationOptions = computed(() => {
traverse(props.organizations || [])
return result
})
</script>
<style scoped>
/* 让表单项标签和输入框呈上下结构 */
:deep(.el-form-item) {
......@@ -680,197 +669,150 @@ const organizationOptions = computed(() => {
flex-direction: column;
align-items: stretch;
}
:deep(.el-form-item__label) {
margin-bottom: 0;
text-align: left !important;
padding: 0 !important;
}
:deep(.el-form-item__content) {
margin-left: 0 !important;
}
.space-y-6 > * + * {
margin-top: 1.5rem;
}
.space-y-4 > * + * {
margin-top: 1rem;
}
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.col-span-2 {
grid-column: span 2 / span 2;
}
.grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-cols-5 {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.gap-4 {
gap: 1rem;
}
.h-10 {
height: 2.5rem;
}
.w-full {
width: 100%;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.text-neutral-500 {
color: #6b7280;
}
.text-neutral-700 {
color: #374151;
}
.text-neutral-900 {
color: #111827;
}
.text-blue-600 {
color: #2563eb;
}
.text-red-500 {
color: #ef4444;
}
.border-neutral-200 {
border-color: #e5e7eb;
}
.border-neutral-300 {
border-color: #d1d5db;
}
.bg-neutral-50 {
background-color: #f9fafb;
}
.rounded {
border-radius: 0.25rem;
}
.rounded-lg {
border-radius: 0.5rem;
}
.border {
border-width: 1px;
}
.border-b {
border-bottom-width: 1px;
}
.p-4 {
padding: 1rem;
}
.p-5 {
padding: 1.25rem;
}
.p-6 {
padding: 1.5rem;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
.pb-2 {
padding-bottom: 0.5rem;
}
.mt-1 {
margin-top: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.mr-1 {
margin-right: 0.25rem;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.gap-2 {
gap: 0.5rem;
}
.gap-3 {
gap: 0.75rem;
}
.overflow-hidden {
overflow: hidden;
}
.overflow-y-auto {
overflow-y: auto;
}
.max-h-\[400px\] {
max-height: 400px;
}
/* 用户管理对话框样式 */
:deep(.user-management-dialog) {
max-height: 90vh;
......@@ -879,30 +821,25 @@ const organizationOptions = computed(() => {
display: flex;
flex-direction: column;
}
:deep(.user-management-dialog .el-dialog__body) {
flex: 1;
overflow-y: auto;
max-height: calc(90vh - 120px); /* 减去头部和底部的空间 */
padding: 24px !important;
}
:deep(.user-management-dialog .el-dialog__header) {
flex-shrink: 0;
padding: 24px 24px 0 24px !important;
}
:deep(.user-management-dialog .el-dialog__footer) {
flex-shrink: 0;
padding: 0 24px 24px 24px !important;
}
/* 搜索输入框样式 - 与订单管理保持一致 */
:deep(.search-input .el-input__wrapper) {
padding-left: 2.5rem !important;
}
:deep(.search-input .el-input__inner) {
padding-left: 0 !important;
}
</style>
</style>
\ No newline at end of file
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>
<template>
<div :class="className || 'relative size-full'">
<svg class="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1024 1024">
<g>
<!-- 角色管理图标 - 用户+加号 -->
<path
d="M471 921.6H225.3c-22.6 0-41-18.4-41-41V757.8c0-112.9 91.9-204.8 204.8-204.8h245.8c43 0 84.3 13.4 119.4 38.7 18.4 13.3 43.9 9.1 57.2-9.2 13.3-18.3 9.1-43.9-9.2-57.2-49.1-35.5-107-54.2-167.4-54.2H389.1C231 471 102.4 599.7 102.4 757.8v122.9c0 67.8 55.1 122.9 122.9 122.9H471c22.6 0 41-18.3 41-41s-18.3-41-41-41zM512 430.1c113.1 0 204.8-91.7 204.8-204.8S625.1 20.5 512 20.5s-204.8 91.7-204.8 204.8S398.9 430.1 512 430.1z m0-327.7c67.8 0 122.9 55.1 122.9 122.9S579.8 348.2 512 348.2 389.1 293 389.1 225.3 444.2 102.4 512 102.4z"
fill="currentColor"
/>
<path
d="M880.6 798.7h-81.9v-81.9c0-22.5-18.4-41-41-41-22.5 0-41 18.4-41 41v81.9h-81.9c-22.5 0-41 18.4-41 41 0 22.5 18.4 41 41 41h81.9v81.9c0 22.5 18.4 41 41 41 22.5 0 41-18.4 41-41v-81.9h81.9c22.5 0 41-18.4 41-41 0-22.5-18.4-41-41-41z"
fill="currentColor"
/>
</g>
</svg>
</div>
</template>
<script setup lang="ts">
interface Props {
className?: string
}
defineProps<Props>()
</script>
......@@ -12,6 +12,14 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import router from './router'
// 引入 API 接口和工具方法
// @ts-ignore
import api from './assets/js/api/interface/index.js'
// @ts-ignore
import stores from './assets/js/stores/index.js'
// @ts-ignore
import commonUtils from './assets/js/const/common.js'
const app = createApp(App)
// 注册Element Plus图标
......@@ -23,4 +31,9 @@ app.use(createPinia())
app.use(router)
app.use(ElementPlus)
// 将 API 接口、请求实例、状态管理和工具方法挂载到全局
app.config.globalProperties.$api = api
app.config.globalProperties.$stores = stores
app.config.globalProperties.$utils = commonUtils
app.mount('#app')
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
{
path: '/role-test',
name: 'role-test',
component: () => import('../views/RoleTestView.vue'),
},
],
})
......
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>
<script setup lang="ts">
import TheWelcome from '../components/TheWelcome.vue'
</script>
<template>
<main>
<TheWelcome />
</main>
</template>
<template>
<div class="min-h-screen bg-gray-50 p-6">
<div class="max-w-7xl mx-auto">
<h1 class="text-2xl font-bold text-gray-900 mb-6">角色管理测试页面</h1>
<RoleManagement
:roles="roles"
:permissions="permissions"
@add-role="handleAddRole"
@update-role="handleUpdateRole"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import RoleManagement from '@/components/RoleManagement.vue'
import { ElMessage } from 'element-plus'
// 类型定义
interface Role {
id: string
name: string
description?: string
level: '地市级' | '区县级' | '一线人员'
permissionIds: string[]
status: '启用' | '禁用'
createTime?: string
}
interface Permission {
id: string
name: string
code: string
description?: string
parentId?: string
children?: Permission[]
}
// 测试数据
const roles = ref<Role[]>([
{
id: 'role-001',
name: '区县全权管理员',
description: '拥有区县级别的所有管理权限,可以管理订单、用户和业务规则',
level: '区县级',
permissionIds: [
'order',
'order:view',
'order:complete',
'order:reward',
'order:approve',
'business',
'business:view',
'business:create',
'business:edit',
'system',
'system:role',
'system:role:view',
'system:role:create',
'system:role:edit',
'system:account',
'system:account:view',
'system:account:create',
'system:account:edit'
],
status: '启用',
createTime: '2025-10-20 09:00:00'
},
{
id: 'role-002',
name: '订单管理员',
description: '负责订单的日常管理和处理',
level: '一线人员',
permissionIds: ['order', 'order:view', 'order:complete', 'order:reward', 'order:approve'],
status: '启用',
createTime: '2025-10-20 09:30:00'
},
{
id: 'role-003',
name: '业务规则管理员',
description: '负责业务规则的配置和维护',
level: '区县级',
permissionIds: ['business', 'business:view', 'business:create', 'business:edit'],
status: '启用',
createTime: '2025-10-20 10:00:00'
}
])
const permissions = ref<Permission[]>([
{
id: 'order',
name: '订单管理',
code: 'order',
description: '订单相关的所有权限',
children: [
{
id: 'order:view',
name: '查看订单',
code: 'order:view',
description: '查看订单列表和详情',
parentId: 'order'
},
{
id: 'order:complete',
name: '填写办结信息',
code: 'order:complete',
description: '填写CRM订单编号和办理备注',
parentId: 'order'
},
{
id: 'order:reward',
name: '填写酬金金额',
code: 'order:reward',
description: '填写和修改实际发放酬金',
parentId: 'order'
},
{
id: 'order:approve',
name: '审核',
code: 'order:approve',
description: '审核通过或驳回订单',
parentId: 'order'
}
]
},
{
id: 'business',
name: '业务规则管理',
code: 'business',
description: '业务规则相关权限',
children: [
{
id: 'business:view',
name: '查看业务规则',
code: 'business:view',
description: '查看业务规则列表',
parentId: 'business'
},
{
id: 'business:create',
name: '创建业务规则',
code: 'business:create',
description: '创建新的业务规则',
parentId: 'business'
},
{
id: 'business:edit',
name: '编辑业务规则',
code: 'business:edit',
description: '修改和停用业务规则',
parentId: 'business'
}
]
},
{
id: 'system',
name: '系统管理',
code: 'system',
description: '系统管理相关权限',
children: [
{
id: 'system:role',
name: '角色管理',
code: 'system:role',
description: '角色管理相关权限',
parentId: 'system',
children: [
{
id: 'system:role:view',
name: '查看角色',
code: 'system:role:view',
description: '查看角色列表',
parentId: 'system:role'
},
{
id: 'system:role:create',
name: '创建角色',
code: 'system:role:create',
description: '创建新角色',
parentId: 'system:role'
},
{
id: 'system:role:edit',
name: '编辑角色',
code: 'system:role:edit',
description: '编辑角色信息和权限',
parentId: 'system:role'
}
]
},
{
id: 'system:account',
name: '账号管理',
code: 'system:account',
description: '账号管理相关权限',
parentId: 'system',
children: [
{
id: 'system:account:view',
name: '查看账号',
code: 'system:account:view',
description: '查看账号列表',
parentId: 'system:account'
},
{
id: 'system:account:create',
name: '创建账号',
code: 'system:account:create',
description: '创建新账号',
parentId: 'system:account'
},
{
id: 'system:account:edit',
name: '编辑账号',
code: 'system:account:edit',
description: '编辑账号信息',
parentId: 'system:account'
}
]
}
]
}
])
// 事件处理
const handleAddRole = (roleData: Omit<Role, 'id' | 'createTime'>) => {
const newRole: Role = {
id: `role-${Date.now()}`,
...roleData,
createTime: new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
roles.value.push(newRole)
ElMessage.success('角色创建成功')
console.log('新增角色:', newRole)
}
const handleUpdateRole = (roleId: string, updates: Partial<Role>) => {
const roleIndex = roles.value.findIndex(role => role.id === roleId)
if (roleIndex !== -1) {
roles.value[roleIndex] = { ...roles.value[roleIndex], ...updates }
ElMessage.success('角色更新成功')
console.log('更新角色:', roleId, updates)
}
}
</script>
<style scoped>
/* 测试页面样式 */
</style>
......@@ -10,9 +10,22 @@ export default defineConfig({
vue(),
// vueDevTools(),
],
base: './',
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
port: 3000,
open: true,
proxy: {
// API 请求代理配置
'/hallserver': {
target: 'http://thall.51xinpai.cn/', // 后端服务地址,根据你的实际情况修改
changeOrigin: true,
rewrite: (path) => path.replace(/^\/hallserver/, '/hallserver')
},
}
},
})
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!