Compare commits

..

No commits in common. "dev" and "main" have entirely different histories.
dev ... main

34 changed files with 150 additions and 1675 deletions

2
.env
View File

@ -17,4 +17,4 @@ VITE_GLOB_AMAP_SECURITY_JS_CODE = 'b85d17b7e0fa98864d495a9a52f162e4'
VITE_GLOB_AMAP_KEY = '0e6910fae6848722b0e57f0c01597499'
# 图片上传展示路径
VITE_APP_IMAGE_URL = 'https://www.cx.cdzhuojing.cn/file/static-objects/img-file/'
VITE_APP_IMAGE_URL = 'https://www.cdzhuojing.cn/file/static-objects/img-file/'

View File

@ -8,9 +8,8 @@ VITE_ROUTER_MODE = hash
# 默认情况下vite 会假设你的应用是被部署在一个域名的根路径上,所以这里 VITE_PUBLIC_PATH 为 '/'
VITE_PUBLIC_PATH = './'
# https://www.cx.cdzhuojing.cn/ebike
# http://192.168.1.18:10010
# https://www.cdzhuojing.cn/ebike
# 请求路径 管理系统/开发环境
VITE_APP_BASE_URL = 'https://www.cx.cdzhuojing.cn/ebike'
VITE_APP_BASE_URL = 'https://www.cdzhuojing.cn/ebike'

View File

@ -11,5 +11,5 @@ VITE_ROUTER_MODE = hash
VITE_PUBLIC_PATH = '/'
# 请求路径 管理系统/开发环境
VITE_APP_BASE_URL = 'https://www.cx.cdzhuojing.cn/ebike'
VITE_APP_BASE_URL = 'https://www.cdzhuojing.cn/ebike'

View File

@ -39,7 +39,6 @@
"@codemirror/lang-vue": "^0.1.3",
"@codemirror/theme-one-dark": "^6.1.2",
"@fingerprintjs/fingerprintjs": "^4.6.2",
"@types/file-saver": "^2.0.7",
"@visactor/vchart": "^1.11.0",
"@visactor/vchart-arco-theme": "^1.11.0",
"@vueuse/core": "^12.4.0",
@ -48,10 +47,8 @@
"axios": "^1.6.8",
"codemirror": "^6.0.1",
"driver.js": "^1.3.1",
"file-saver": "^2.0.5",
"fingerprintjs2": "^2.1.4",
"jsbarcode": "^3.11.6",
"jszip": "^3.10.1",
"nprogress": "^0.2.0",
"pinia": "^2.3.0",
"pinia-plugin-persistedstate": "^3.2.1",

View File

@ -73,9 +73,9 @@ service.interceptors.response.use(
}
},
function (error: any) {
// console.log(error);
localStorage.removeItem("user-info");
router.replace("/login");
// TUDU这里逻辑不符合当前系统后续改
// localStorage.removeItem("user-info");
// router.push("/login");
return Promise.reject(error);
}
);

View File

@ -1,21 +0,0 @@
import axios from "@/api";
import { ApplyChangeCarListParams, ProcessUserReportParams } from "./types";
// 申请换车点列表
export const getApplyChangeCarListAPI = (params: ApplyChangeCarListParams) => {
return axios({
url: "/operations/ebikeReportRecord/page",
method: "get",
params
});
};
// 处理用户上报记录
export const handleApplyChangeCarAPI = (data: ProcessUserReportParams) => {
return axios({
url: "/operations/ebikeReportRecord/dealReportRecord",
method: "post",
data
});
};

View File

@ -1,25 +0,0 @@
interface ListType {
pageNum: number;
pageSize: number;
}
// 申请换车点列表参数
export interface ApplyChangeCarListParams extends ListType {
recordStatus?: number;
}
// 处理用户上报记录参数
type Coordinate = [number, number];
interface RegionPolygon {
type: "polygon" | "point";
coordinates: Coordinate[];
}
export interface ProcessUserReportParams {
recordId: string;
staffId: string;
recordStatus: 1 | 2; //处理状态 1-同意申请 2-拒绝申请
recordReason?: string;
siteName?: string;
sitePolygon?: RegionPolygon;
siteType?: number;
}

View File

@ -13,11 +13,8 @@ export const getUserRefundListAPI = (data: RefundLisType) => {
// 获取退款订单详情
export const getRefundOrderDetailAPI = (refundId: string) => {
return axios({
url: "operations/ebikeRefundReview/refundOrderDetail",
method: "get",
params: {
refundId
}
url: "operations/ebikeRefundReview/refundOrderDetail/" + refundId,
method: "get"
});
};
@ -29,41 +26,3 @@ export const reviewRefundApplicationAPI = (data: reviewRefundApplicationType) =>
data
});
};
// 获取骑行中与待支付的订单列表
export const getUserOrderListAPI = (params: { bikeCode: string; pageNum: number; pageSize: number }) => {
return axios({
url: "operations/statistics/getDiffOperatorOrderList",
method: "get",
params
});
};
//根据车辆编号修改当前订单金额
export const updateOrderAmountByBikeCodeAPI = (data: { bikeCode: string; price: number }) => {
return axios({
url: "operations/statistics/updateOrderAmount",
method: "post",
data
});
};
// 结束用户骑行订单
export const endUserRideOrderAPI = (data: { bikeCode: string }) => {
return axios({
url: "operations/statistics/closeOrder",
method: "post",
data
});
};
// 车辆详情
export const getBikeDetailAPI = (bikeCode: string) => {
return axios({
url: "operations/ebikeBikeOrder/bikeInfo",
method: "get",
params: {
bikeCode
}
});
};

View File

@ -11,7 +11,7 @@ export interface RefundLisType {
export interface reviewRefundApplicationType {
staffId: string;
reviewId: string;
refundId: number;
operate: number;
reason?: string;
refundMethod: number;

View File

@ -1,20 +0,0 @@
import axios from "@/api";
import { OperatorConfigFormType } from "./types";
// 运营商配置添加更新
export const addOrUpdateOperatorConfigAPI = (data: OperatorConfigFormType) => {
return axios({
url: "/operations/ebikeCarrierConfiguration/saveOrUpdate",
method: "post",
data
});
};
//根据运营商id查询配置信息
export const getOperatorConfigByOperatorIdAPI = (operatorId: string) => {
return axios({
url: "/operations/ebikeCarrierConfiguration/getByOperatorId",
method: "get",
params: { operatorId }
});
};

View File

@ -1,8 +0,0 @@
//运营商配置添加更新配置
export interface OperatorConfigFormType {
operatorId: string; //运营商id
dispatchDuration: number | undefined; //配置项时长
rideDuration: number | undefined; //配置项骑行时长
inspectionIntervalDuration: number | undefined; //巡检间隔时长(日)
operatorPhone: string; //运营商手机号
}

View File

@ -11,8 +11,7 @@ import {
AddDictFormType,
DictItemListParams,
AddDictItemFormType,
saveImageType,
OnlineUpdateParams
saveImageType
} from "./types";
// 获取菜单数据
@ -365,12 +364,3 @@ export const updateImageAPI = (data: saveImageType) => {
data
});
};
// 在线模块升级
export const onlineUpgradeAPI = (data: OnlineUpdateParams) => {
return axios({
url: "/operations/ebikeEcuInfo/upgrade",
method: "post",
data
});
};

View File

@ -99,13 +99,3 @@ export interface saveImageType {
imgCode?: string; // 图片编码
fileUrl: string; // 图片地址
}
// 远程升级
export interface OnlineUpdateParams {
ecuSn?: string
bikeCode?: string
ecuBrand: string
url: string
fullUpgrade: boolean
type: number
}

1
src/components.d.ts vendored
View File

@ -7,6 +7,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
copy: typeof import('./components/s-layout-search/index copy.vue')['default']
'Index copy': typeof import('./components/s-layout-search/index copy.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']

View File

@ -1,6 +1,4 @@
import { ref, Ref } from "vue";
import JSZip from "jszip";
import { saveAs } from "file-saver";
type DownloadStatus = "idle" | "loading" | "success" | "error";
@ -58,48 +56,35 @@ export function useImageDownloader(): any {
};
// 批量下载(并行)
const downloadImages = async (urls: string[], customFileNames?: string[]): Promise<void> => {
// 可选:更新状态(如果你用 Vue 的 ref
// downloadStatus.value = "loading";
const downloadImages = async (urls: string[], customFileNames?: string[]): Promise<DownloadResult[]> => {
downloadStatus.value = "loading";
batchResults.value = [];
const zip = new JSZip();
const errors: string[] = [];
const promises = urls.map(async (url, index) => {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const blob = await response.blob();
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = customFileNames?.[index] || extractFileName(url);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
return { url, status: "success" } as DownloadResult;
} catch (error) {
console.error(`Failed to download ${url}:`, error);
return { url, status: "error", message: (error as Error).message } as DownloadResult;
}
});
// 并发获取所有图片(可加并发控制,但 30 个一般没问题)
await Promise.all(
urls.map(async (url, index) => {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const blob = await res.blob();
let fileName = customFileNames?.[index] || extractFileName(url);
// 防止文件名重复(简单处理)
if (zip.file(fileName)) {
const ext = fileName.includes(".") ? fileName.split(".").pop() : "";
const name = fileName.replace(new RegExp(`\\.${ext}$`), "");
fileName = `${name}_${Date.now()}.${ext || "jpg"}`;
}
zip.file(fileName, blob);
} catch (err) {
console.error("Download failed:", url, err);
errors.push(url);
}
})
);
// 生成 ZIP Blob
const zipBlob = await zip.generateAsync({ type: "blob" });
// 触发下载 —— 这是安全的、用户可见的一次性下载
saveAs(zipBlob, "车辆二维码.zip");
// 可选:提示错误
if (errors.length > 0) {
alert(`成功打包 ${urls.length - errors.length} 张图片。\n失败${errors.length}`);
}
// downloadStatus.value = "success";
const results = await Promise.all(promises);
batchResults.value = results;
downloadStatus.value = "success";
return results;
};
return {
downloadStatus,
batchResults,

View File

@ -132,8 +132,7 @@ const handleDownloadImage = async (record: any) => {
//
const batchDownload = async () => {
const urls = select_list.value.map(item => item.bikeQr);
const names = select_list.value.map(item => `车辆二维码-${item.bikeQrCode}.png`);
console.log(urls, names);
const names = select_list.value.map(item => `电池二维码-${item.bikeQrCode}.png`);
await downloadImages(urls, names);
};

View File

@ -13,7 +13,6 @@ const emits = defineEmits(["ok"]);
const open = ref<boolean>(false);
//
const applyPoint = reactive({ lng: undefined, lat: undefined });
const map = shallowRef<any>(null);
const mouseTool = shallowRef<any>(null);
const polyEditor = shallowRef<any>(null);
@ -24,7 +23,6 @@ const type = ref<string>("add");
const currentPolygon = shallowRef<any>(null); //
const operationArea = ref<any[]>([]); //
const operationPolygon = shallowRef<any>(null); //
const applyPointMarker = shallowRef<any>(null); // Marker
let originalPath: any[] = []; //
const options__polygon = {
@ -107,14 +105,9 @@ const initMap = () => {
center = [104.066541, 30.572269]; //
}
//
if (applyPoint.lat && applyPoint.lng) {
center = [applyPoint.lng, applyPoint.lat];
}
//
map.value = new mapRes.Map("container", {
zoom: applyPoint.lat && applyPoint.lng ? 14 : 12,
zoom: 12,
center: center
});
@ -133,37 +126,12 @@ const initMap = () => {
});
};
/**
* 绘制申请点 Marker
*/
function drawApplyPointMarker() {
if (!map.value || !applyPoint.lng || !applyPoint.lat) return;
// Marker
if (applyPointMarker.value) {
map.value.remove(applyPointMarker.value);
}
applyPointMarker.value = new mapAmap.value.Marker({
position: [applyPoint.lng, applyPoint.lat],
title: "申请点位",
icon: "https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png", //
zIndex: 999
});
map.value.add(applyPointMarker.value);
}
/**
* 打开地图
**/
async function openMap(parmas: any) {
console.log("openMap parmas:", parmas);
const { regionPoints, sitePoints } = parmas;
if (parmas?.applyPoints) {
applyPoint.lat = parmas.applyPoints.latitude;
applyPoint.lng = parmas.applyPoints.longitude;
}
//
if (regionPoints?.length > 0) {
@ -177,11 +145,6 @@ async function openMap(parmas: any) {
//
clearAll();
//
if (applyPoint.lat && applyPoint.lng) {
drawApplyPointMarker();
}
//
if (sitePoints) {
path.value = sitePoints;
@ -572,11 +535,6 @@ function afterClose() {
if (currentPolygon.value) {
map.value.remove(currentPolygon.value);
}
// Marker
if (applyPointMarker.value) {
map.value.remove(applyPointMarker.value);
applyPointMarker.value = null;
}
if (operationPolygon.value) {
map.value.remove(operationPolygon.value);
}
@ -591,12 +549,9 @@ function afterClose() {
path.value = [];
currentPolygon.value = null;
operationPolygon.value = null;
applyPointMarker.value = null; // 👈
polyEditor.value = null;
mouseTool.value = null;
operationArea.value = []; //
applyPoint.lat = undefined;
applyPoint.lng = undefined;
type.value = "add";
}

View File

@ -1,69 +0,0 @@
<script lang="ts" setup>
declare global {
interface Window {
_AMapSecurityConfig: any;
}
}
import AMapLoader from "@amap/amap-jsapi-loader";
const { VITE_GLOB_AMAP_KEY, VITE_GLOB_AMAP_SECURITY_JS_CODE, VITE_APP_IMAGE_URL } = import.meta.env;
const mapAmap = shallowRef<any>(null);
const map = shallowRef<any>(null);
/**
* 地图容器初始化
**/
const initMap = (location: any) => {
window._AMapSecurityConfig = {
securityJsCode: VITE_GLOB_AMAP_SECURITY_JS_CODE
};
AMapLoader.load({
key: VITE_GLOB_AMAP_KEY,
version: "2.0",
plugins: [
"AMap.Marker",
"AMap.Pixel",
"AMap.Polyline",
"AMap.Geolocation",
"AMap.MouseTool",
"AMap.Polygon",
"AMap.PolygonEditor"
]
}).then((mapRes: any) => {
mapAmap.value = mapRes;
const applyPoint = location || {};
//
map.value = new mapRes.Map("container", {
zoom: applyPoint.latitude && applyPoint.longitude ? 14 : 12,
center: applyPoint.latitude && applyPoint.longitude ? [applyPoint.longitude, applyPoint.latitude] : undefined
});
const customIcon = new mapAmap.value.Icon({
size: new mapAmap.value.Size(50, 60), //
imageSize: new mapAmap.value.Size(40, 40), //
image: `${VITE_APP_IMAGE_URL}45cdabf8a98f4268b84f25df5f2c97b6.png`
});
const marker = new mapAmap.value.Marker({
position: [applyPoint.longitude, applyPoint.latitude],
title: "申请点位",
icon: customIcon,
zIndex: 999
});
map.value.add(marker);
});
};
defineExpose({
initMap
});
</script>
<template>
<div id="container" style="height: 100%; width: 100%"></div>
</template>
<style scoped lang="scss"></style>

View File

@ -1,437 +0,0 @@
<script lang="ts" setup>
import { getApplyChangeCarListAPI, handleApplyChangeCarAPI } from "@/api/modules/declare";
import { deepClone, dictTranslate } from "@/utils";
import { getAllRegionAPI, getRegionDetailAPI } from "@/api/modules/region/index";
import { useSystemStore } from "@/store/modules/system";
import { getOperatorAllListAPI } from "@/api/modules/system";
import { useUserInfoStore } from "@/store/modules/user-info";
import SiteMap from "@/views/component/SiteMap/SiteMap.vue";
import { PointDataHandler } from "@/utils/map-tools";
const userStores = useUserInfoStore();
const system = useSystemStore();
const form = ref({
recordStatus: undefined
});
const regionStatusList = ref<any>([]);
const operatorAllList = ref<any>([]);
const operation_list = ref<any>([]);
const siteType_list = ref<any>([]);
//
const moduleList = ref([]);
const loading = ref(false);
const pagination = ref({
current: 1,
total: 0,
pageSize: 10,
showPageSize: true,
showTotal: true
});
//
const open = ref<boolean>(false);
const title = ref<string>("审核");
const RegionalMapRef = ref<any>(null);
const loadingOk = ref<boolean>(false);
const addFormRef = ref();
const addForm = ref<any>({
recordId: "",
recordReason: "",
latitude: undefined,
longitude: undefined,
staffId: "",
operatorId: "",
siteName: "",
regionId: "",
siteType: "",
sitePolygon: {
type: "Polygon",
coordinates: []
}
});
const rules = {
siteName: [
{
required: true,
message: "请输入站点名称",
trigger: "blur"
}
],
regionId: [
{
required: true,
message: "请选择运营区",
trigger: "change"
}
],
siteType: [
{
required: true,
message: "请选择运营区",
trigger: "change"
}
],
operatorId: [
{
required: true,
message: "请选择运营商",
trigger: "change"
}
],
"sitePolygon.coordinates": [
{
required: true,
validator: (value: any, cb: any) => {
console.log(value);
if (value.length === 0) {
cb("请编辑区域电子围栏");
} else {
cb();
}
}
}
]
};
const search = () => {
pagination.value.current = 1;
getApplyChangeCarList();
};
const reset = () => {
form.value = {
recordStatus: undefined
};
search();
};
//
const getAllOperatorList = async () => {
return new Promise<void>(async resolve => {
try {
const res: any = await getOperatorAllListAPI();
if (res.code === 200) {
operatorAllList.value = res.data;
resolve();
}
} catch (error) {
console.error("获取运营商列表失败:", error);
resolve();
}
});
};
//
const getAllRegion = async () => {
return new Promise<void>(async resolve => {
try {
const res: any = await getAllRegionAPI();
if (res.code === 200) {
operation_list.value = res.data;
resolve();
}
} catch (error) {
console.error("获取运营商列表失败:", error);
resolve();
}
});
};
const handlePageChange = (page: number) => {
pagination.value.current = page;
getApplyChangeCarList();
};
const handlePageSizeChange = (pageSize: number) => {
pagination.value.pageSize = pageSize;
pagination.value.current = 1;
getApplyChangeCarList();
};
// tag
const tagColor = (record: any) => {
const { recordStatus } = record;
switch (recordStatus) {
case 0:
return "gray";
case 1:
return "green";
case 2:
return "red";
default:
return "gray";
}
};
const getApplyChangeCarList = async () => {
loading.value = true;
const query: any = {
pageNum: pagination.value.current,
pageSize: pagination.value.pageSize
};
if (form.value.recordStatus) {
query.recordStatus = form.value.recordStatus;
}
try {
const res: any = await getApplyChangeCarListAPI(query);
if (res.code !== 200) {
return;
}
const { records, pageNumber, pageSize } = res.data;
pagination.value.current = pageNumber;
pagination.value.pageSize = pageSize;
pagination.value.total = res.data.totalRow;
moduleList.value = records;
} catch (error) {
console.error("获取列表失败:", error);
} finally {
loading.value = false;
}
};
//
const dictFormat = (list: any[], key: string, value: string | number, valueKey: string = "dicValueName") => {
return dictTranslate(list, key, value, valueKey);
};
const handleApplyChangeCar = (record: any) => {
open.value = true;
// console.log(record);
const { operatorId, regionId, location, recordId } = record;
addForm.value.recordId = recordId;
addForm.value.operatorId = operatorId;
addForm.value.regionId = regionId;
addForm.value.longitude = location.longitude;
addForm.value.latitude = location.latitude;
};
//
const resetForm = () => {
addForm.value = deepClone({
recordId: "",
recordReason: "",
staffId: "",
operatorId: "",
siteName: "",
regionId: "",
latitude: undefined,
longitude: undefined,
siteType: "",
sitePolygon: {
type: "Polygon",
coordinates: []
}
});
if (addFormRef.value) {
addFormRef.value.resetFields();
}
};
//
const handleOpenMap = async () => {
//
if (!addForm.value.regionId) {
arcoMessage("warning", "请先选择运营区域");
return;
}
try {
const res = await getRegionDetailAPI(addForm.value.regionId);
if (res) {
const { regionPolygon } = res.data;
// console.log(regionPolygon);
let path: any =
addForm.value.sitePolygon.coordinates.length === 0
? {
regionPoints: PointDataHandler.restoreForDisplay(regionPolygon.coordinates),
sitePoints: null
}
: {
regionPoints: PointDataHandler.restoreForDisplay(regionPolygon.coordinates),
sitePoints: deepClone(addForm.value.sitePolygon.coordinates)
};
path.applyPoints = {
latitude: addForm.value.latitude,
longitude: addForm.value.longitude
};
RegionalMapRef.value?.openMap(path);
}
} catch (error: any) {
console.error("获取区域点位失败:", error);
arcoMessage("error", "获取区域点位失败");
}
};
//
const checkOrder = async (recordStatus: number) => {
const { staffId } = userStores.account;
let parmas: any = {};
if (recordStatus === 1) {
let status = await addFormRef.value.validate();
if (status) return false;
const coordinates = PointDataHandler.prepareForUpload(addForm.value.sitePolygon.coordinates);
parmas = deepClone({
recordId: addForm.value.recordId,
staffId,
recordStatus,
recordReason: addForm.value.recordReason,
siteName: addForm.value.siteName,
siteType: addForm.value.siteType,
sitePolygon: {
type: "Polygon",
coordinates
}
});
} else {
parmas = deepClone({
recordId: addForm.value.recordId,
staffId,
recordStatus,
recordReason: addForm.value.recordReason
});
}
loadingOk.value = true;
try {
const res = await handleApplyChangeCarAPI(parmas);
arcoMessage("success", "已审核");
resetForm();
open.value = false;
search();
} catch (err) {
console.error(err);
} finally {
loadingOk.value = false;
}
};
const afterClose = () => {
open.value = false;
resetForm();
};
//
const getPoint = (paths: any[]) => {
addForm.value.sitePolygon.coordinates = paths;
};
onMounted(() => {
getApplyChangeCarList();
});
onBeforeMount(async () => {
regionStatusList.value = deepClone(system.getDictByCode("ApplicationVehicleStatus"));
siteType_list.value = deepClone(system.getDictByCode("siteType"));
await getAllOperatorList();
await getAllRegion();
});
</script>
<template>
<div class="snow-page">
<div class="snow-inner">
<s-layout-tools>
<template #left>
<a-space wrap>
<a-select :style="{ width: '250px' }" placeholder="审核状态" v-model="form.recordStatus" allow-clear>
<a-option v-for="it in regionStatusList" :key="it.dicValue" :value="it.dicValue">{{ it.dicValueName }}</a-option>
</a-select>
<a-button type="primary" @click="search">
<template #icon><icon-search /></template>
<span>查询</span>
</a-button>
<a-button @click="reset">
<template #icon><icon-refresh /></template>
<span>重置</span>
</a-button>
</a-space>
</template>
</s-layout-tools>
<a-table
row-key="refundId"
:data="moduleList"
:bordered="{ cell: true }"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<template #columns>
<a-table-column title="序号" :width="60" align="center">
<template #cell="cell">
<span>{{ (pagination.current - 1) * pagination.pageSize + cell.rowIndex + 1 }}</span>
</template>
</a-table-column>
<a-table-column title="申请地址" data-index="address" align="center">
<template #cell="{ record }">
{{ record.address || "--" }}
</template>
</a-table-column>
<a-table-column title="审核备注" data-index="recordReason" align="center">
<template #cell="{ record }">
{{ record.recordReason || "--" }}
</template>
</a-table-column>
<a-table-column title="申请时间" data-index="createTime" align="center"></a-table-column>
<a-table-column title="审核时间" data-index="recordTime" align="center"></a-table-column>
<a-table-column title="审核状态" data-index="recordStatus" align="center">
<template #cell="{ record }">
<a-tag :color="tagColor(record)" bordered>{{
dictFormat(regionStatusList, "dicValue", record.recordStatus)
}}</a-tag>
</template>
</a-table-column>
<a-table-column title="操作" align="center" :fixed="'right'">
<template #cell="{ record }">
<a-space>
<a-button v-if="record.recordStatus === 0" type="primary" size="mini" @click="handleApplyChangeCar(record)">
<span>审核</span>
</a-button>
</a-space>
</template>
</a-table-column>
</template>
</a-table>
</div>
<!-- 模态框 -->
<a-modal v-model:visible="open" :width="900" @close="afterClose" @cancel="afterClose">
<template #title> {{ title }} </template>
<a-form ref="addFormRef" :model="addForm" :rules="rules">
<a-form-item label="站点名称" field="siteName" validate-trigger="blur">
<a-input placeholder="请输入站点名称" allow-clear v-model="addForm.siteName" />
</a-form-item>
<a-form-item field="operatorId" label="运营商" validate-trigger="change">
<a-select v-model="addForm.operatorId" placeholder="请选择运营商" disabled>
<a-option v-for="it in operatorAllList" :key="it.operatorId" :value="it.operatorId">{{ it.operatorName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="regionId" label="运营区域" validate-trigger="change">
<a-select v-model="addForm.regionId" placeholder="请选择运营区域" allow-search disabled>
<a-option v-for="it in operation_list" :key="it.regionId" :value="it.regionId">{{ it.regionName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="siteType" label="运营区域类型" validate-trigger="change">
<a-radio-group v-model="addForm.siteType">
<a-radio v-for="it in siteType_list" :key="it.dicId" :value="it.dicValue">{{ it.dicValueName }}</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="站点电子围栏" field="sitePolygon.coordinates" validate-trigger="blur">
<a-tag bordered color="orange" v-if="addForm.sitePolygon.coordinates.length === 0">未绘制</a-tag>
<a-tag bordered color="green" v-else>已绘制</a-tag>
<a-link @click="handleOpenMap" style="margin-left: 15px">编辑地图</a-link>
</a-form-item>
</a-form>
<template #footer>
<div style="margin-bottom: 20px">
<a-textarea v-model="addForm.recordReason" placeholder="审核意见" allow-clear class="textarea_box" />
</div>
<div class="footer-btn">
<a-button type="primary" status="danger" @click="checkOrder(2)" :loading="loadingOk">驳回</a-button>
<a-button type="primary" style="margin-left: 50px" status="success" @click="checkOrder(1)" :loading="loadingOk">
通过
</a-button>
</div>
</template>
</a-modal>
<SiteMap ref="RegionalMapRef" @ok="getPoint" />
</div>
</template>

View File

@ -52,8 +52,8 @@ let userStores = useUserInfoStore();
const routeStore = useRouteConfigStore();
const router = useRouter();
const form = ref({
username: "",
password: ""
username: "admin1",
password: "123456"
});
const rules = ref({
username: [

View File

@ -4,9 +4,9 @@
<div class="login">
<LoginBanner v-if="isPc" />
<div class="login_box">
<div class="login_title">欢迎登录电单车管理系统</div>
<div class="login_title_desc">高效管理电单车运营实时掌握车辆动态</div>
<div class="login_title_desc">智能调度助力城市绿色出行</div>
<div class="login_title">Welcome Back</div>
<div class="login_title_desc">国际化路由配置状态管理应有尽有</div>
<div class="login_title_desc">丰富的的页面模板覆盖大多数典型业务场景</div>
<LoginForm @isLoading="state => (loading = state)" />
</div>
</div>

View File

@ -1,77 +0,0 @@
<script lang="ts" setup>
import { deepClone } from "@/utils";
import { updateOrderAmountByBikeCodeAPI } from "@/api/modules/order";
const formData = ref({
bikeCode: "",
orderId: "",
actualAmount: 0,
price: 0
});
const init = (data: any) => {
formData.value = {
...deepClone(data),
price: 0
};
};
const updateOrderAmountByBikeCode = async () => {
return new Promise(async (resolve, reject) => {
try {
const res: any = await updateOrderAmountByBikeCodeAPI({
bikeCode: formData.value.bikeCode,
price: formData.value.price
});
resolve(res.data);
} catch (error) {
console.error("修改订单金额失败:", error);
reject(error);
}
});
};
defineExpose({
init,
updateOrderAmountByBikeCode
});
</script>
<template>
<div>
<a-row class="grid-demo" :gutter="[24, 40]">
<a-col :span="12">
<div>车辆编号{{ formData.bikeCode }}</div>
</a-col>
<a-col :span="12">
<div>订单编号{{ formData.orderId }}</div>
</a-col>
</a-row>
<a-row class="grid-demo" :gutter="[24, 40]">
<a-col :span="12">
<div>实付金额{{ formData.actualAmount }}</div>
</a-col>
</a-row>
<a-row class="grid-demo" :gutter="[24, 40]">
<a-col :span="12">
<div>
修改金额
<a-input-number
v-model="formData.price"
:style="{ width: '200px' }"
placeholder="Please Enter"
:default-value="0"
:min="0"
:step="0.5"
:precision="2"
class="input-demo"
/>
</div>
</a-col>
</a-row>
</div>
</template>
<style lang="scss" scoped>
.container {
width: 100%;
}
</style>

View File

@ -1,54 +0,0 @@
<script lang="ts" setup>
import { deepClone } from "@/utils";
import { endUserRideOrderAPI } from "@/api/modules/order";
const formData = ref({
bikeCode: "",
orderId: "",
actualAmount: 0,
price: 0
});
const init = (data: any) => {
formData.value = {
...deepClone(data),
price: 0
};
};
const endUserRideOrder = async () => {
return new Promise(async (resolve, reject) => {
try {
const res: any = await endUserRideOrderAPI({
bikeCode: formData.value.bikeCode
});
resolve(res.data);
} catch (error) {
console.error("修改订单金额失败:", error);
reject(error);
}
});
};
defineExpose({
init,
endUserRideOrder
});
</script>
<template>
<div>
<a-row class="grid-demo" :gutter="[24, 40]">
<a-col :span="12">
<div>车辆编号{{ formData.bikeCode }}</div>
</a-col>
<a-col :span="12">
<div>订单编号{{ formData.orderId }}</div>
</a-col>
</a-row>
</div>
</template>
<style lang="scss" scoped>
.container {
width: 100%;
}
</style>

View File

@ -1,279 +0,0 @@
<script lang="ts" setup>
import { getUserOrderListAPI, getBikeDetailAPI } from "@/api/modules/order";
import { dictTranslate } from "@/utils";
import { getOperatorAllListAPI } from "@/api/modules/system";
import changePriceModal from "./components/changePriceModal.vue";
import closeBikeModal from "./components/closeBikeModal.vue";
import { Message } from "@arco-design/web-vue";
import bikeMap from "../../component/bikeMap/bikeMap.vue";
const form = ref<any>({
bikeCode: ""
});
//
const operatorAllList = ref<any>([]);
const moduleList = ref([]);
const loading = ref(false);
const pagination = ref({
current: 1,
total: 0,
pageSize: 10,
showPageSize: true,
showTotal: true
});
//
const open = ref<boolean>(false);
const title = ref<string>("");
const componenType = ref<string>("changePrice");
const closeBikeModalRef = ref<any>(null);
const changePriceModalRef = ref<any>(null);
const bikeMapRef = ref<any>(null);
const loadingOk = ref(false);
const search = () => {
pagination.value.current = 1;
getUserOrderList();
};
const reset = () => {
form.value = {
bikeCode: ""
};
search();
};
const getUserOrderList = async () => {
loading.value = true;
try {
const res: any = await getUserOrderListAPI({
bikeCode: form.value.bikeCode,
pageNum: pagination.value.current,
pageSize: pagination.value.pageSize
});
loading.value = false;
if (res.code !== 200) {
return;
}
const { records, pageNumber, pageSize } = res.data;
pagination.value.current = pageNumber;
pagination.value.pageSize = pageSize;
pagination.value.total = res.data.totalRow;
moduleList.value = records;
} catch (error) {
console.error("获取列表失败:", error);
} finally {
loading.value = false;
}
};
const handlePageChange = (page: number) => {
pagination.value.current = page;
getUserOrderList();
};
const handlePageSizeChange = (pageSize: number) => {
pagination.value.pageSize = pageSize;
pagination.value.current = 1;
getUserOrderList();
};
//
const dictFormat = (list: any[], key: string, value: string | number, valueKey: string = "dicValueName") => {
return dictTranslate(list, key, value, valueKey);
};
//
const getAllOperatorList = async () => {
return new Promise<void>(async resolve => {
try {
const res: any = await getOperatorAllListAPI();
if (res.code === 200) {
operatorAllList.value = res.data;
resolve();
}
} catch (error) {
console.error("获取运营商列表失败:", error);
resolve();
}
});
};
const afterClose = () => {
open.value = false;
};
const handleOpenModal = async (record: any, type: string) => {
Message.loading("正在获取车辆详情...");
try {
const { data } = await getBikeDetailAPI(record.bikeCode);
const { location } = data;
bikeMapRef.value.initMap(location);
componenType.value = type;
open.value = true;
if (type === "changePrice") {
title.value = "修改价格";
changePriceModalRef.value.init(record);
} else if (type === "closeBike") {
closeBikeModalRef.value.init(record);
title.value = "远程锁车";
}
} catch (error) {
console.error("获取车辆详情失败:", error);
return;
} finally {
Message.clear();
}
};
const comfirmChangePrice = async () => {
loadingOk.value = true;
try {
await changePriceModalRef.value.updateOrderAmountByBikeCode();
arcoMessage("success", "修改订单金额成功");
afterClose();
getUserOrderList();
} catch (error) {
console.error("修改订单金额失败:", error);
} finally {
loadingOk.value = false;
}
};
const comfirmClostBike = async () => {
try {
await closeBikeModalRef.value.endUserRideOrder();
arcoMessage("success", "远程锁车成功");
afterClose();
getUserOrderList();
} catch (error) {
console.error("远程锁车失败:", error);
}
};
onMounted(() => {
getUserOrderList();
});
onBeforeMount(async () => {
await getAllOperatorList();
});
</script>
<template>
<div class="snow-page">
<div class="snow-inner">
<s-layout-tools>
<template #left>
<a-space wrap>
<a-input :style="{ width: '200px' }" v-model="form.bikeCode" placeholder="车辆编号" allow-clear />
<a-button type="primary" @click="search">
<template #icon><icon-search /></template>
<span>查询</span>
</a-button>
<a-button @click="reset">
<template #icon><icon-refresh /></template>
<span>重置</span>
</a-button>
</a-space>
</template>
</s-layout-tools>
<a-table
row-key="refundId"
:data="moduleList"
:bordered="{ cell: true }"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<template #columns>
<a-table-column title="序号" :width="60" align="center">
<template #cell="cell">
<span>{{ (pagination.current - 1) * pagination.pageSize + cell.rowIndex + 1 }}</span>
</template>
</a-table-column>
<a-table-column title="车辆编号" data-index="bikeCode" align="center"></a-table-column>
<a-table-column title="订单编号" data-index="orderId" align="center"></a-table-column>
<a-table-column title="运营商" data-index="operatorId" align="center">
<template #cell="{ record }">
<span>{{ dictFormat(operatorAllList, "operatorId", record.operatorId, "operatorName") }}</span>
</template>
</a-table-column>
<a-table-column title="实付金额" data-index="actualAmount" align="center">
<template #cell="{ record }">
<span>{{ record.actualAmount }} </span>
</template>
</a-table-column>
<a-table-column title="订单创建时间" data-index="startTime" align="center"></a-table-column>
<a-table-column title="操作" align="center" :fixed="'right'">
<template #cell="{ record }">
<a-space>
<a-button type="primary" size="mini" @click="handleOpenModal(record, 'changePrice')">
<span>修改价格</span>
</a-button>
<a-button status="danger" type="primary" size="mini" @click="handleOpenModal(record, 'closeBike')">
<span>远程锁车</span>
</a-button>
</a-space>
</template>
</a-table-column>
</template>
</a-table>
</div>
<!-- 模态框 -->
<a-modal v-model:visible="open" :width="900" @close="afterClose">
<template #title> {{ title }} </template>
<div class="container">
<div class="mapContent">
<bikeMap ref="bikeMapRef" />
</div>
<div v-show="componenType === 'changePrice'">
<changePriceModal ref="changePriceModalRef" />
</div>
<div v-show="componenType === 'closeBike'">
<closeBikeModal ref="closeBikeModalRef" />
</div>
</div>
<template #footer>
<div class="footer-btn">
<a-button
v-if="componenType === 'changePrice'"
type="primary"
style="margin-right: 20px"
:loading="loadingOk"
@click="comfirmChangePrice"
>
确认修改
</a-button>
<a-button
v-else-if="componenType === 'closeBike'"
type="primary"
style="margin-right: 20px"
@click="comfirmClostBike"
:loading="loadingOk"
>
确认锁车
</a-button>
<a-button @click="afterClose">取消</a-button>
</div>
</template>
</a-modal>
</div>
</template>
<style scoped lang="scss">
.mapContent {
width: 100%;
height: 400px;
margin-bottom: 20px;
}
.footer-btn {
width: 100%;
display: flex;
justify-content: center;
}
</style>

View File

@ -20,7 +20,6 @@ const moduleList = ref([]);
const loading = ref(false);
const pagination = ref({
current: 1,
total: 0,
pageSize: 10,
showPageSize: true,
showTotal: true
@ -32,7 +31,7 @@ const title = ref<string>("");
const loadingOk = ref<boolean>(false);
const addFormRef = ref();
const addForm = ref<any>({
reviewId: "",
refundId: "",
reason: "",
operate: -1,
refundMethod: 0
@ -88,11 +87,11 @@ const getUserRefundList = async () => {
if (res.code !== 200) {
return;
}
console.log(res);
const { records, pageNumber, pageSize } = res.data;
pagination.value.current = pageNumber;
pagination.value.pageSize = pageSize;
pagination.value.total = res.data.totalRow;
moduleList.value = records;
} catch (error) {
console.error("获取员工列表失败:", error);
@ -103,20 +102,20 @@ const getUserRefundList = async () => {
const auditOrder = (record: any) => {
title.value = "退款审核";
getRefundOrderDetail(record.refundId, record.reviewId);
getRefundOrderDetail(record.refundId);
};
const handleDetail = (record: any) => {
title.value = "退款详情";
getRefundOrderDetail(record.refundId, record.reviewId);
getRefundOrderDetail(record.refundId);
};
const getRefundOrderDetail = async (refundId: string, reviewId: string) => {
const getRefundOrderDetail = async (refundId: string) => {
try {
const res: any = await getRefundOrderDetailAPI(refundId);
if (res.code == 200) {
open.value = true;
refundDetail.value = res.data;
addForm.value.reviewId = reviewId;
addForm.value.refundId = refundId;
}
} catch (error) {
console.error("获取退款订单详情失败:", error);
@ -128,6 +127,7 @@ const checkOrder = async (operate: number) => {
const userInfo = localStorage.getItem("user-info");
let staffId = "";
if (userInfo) {
console.log(JSON.parse(userInfo));
staffId = JSON.parse(userInfo).account.staffId;
} else {
console.warn("未找到 user-info 信息");
@ -135,7 +135,7 @@ const checkOrder = async (operate: number) => {
try {
const res: any = await reviewRefundApplicationAPI({
reviewId: addForm.value.reviewId,
refundId: addForm.value.refundId,
operate,
staffId,
reason: addForm.value.reason,
@ -226,11 +226,11 @@ onBeforeMount(async () => {
<a-table-column title="订单编号" data-index="orderId" align="center"></a-table-column>
<a-table-column title="用户手机号" data-index="mobile" align="center"></a-table-column>
<a-table-column title="申请退款金额" data-index="refundApply" align="center"></a-table-column>
<!-- <a-table-column title="订单状态" data-index="processStatus" align="center">
<a-table-column title="订单状态" data-index="processStatus" align="center">
<template #cell="{ record }">
<span>{{ dictFormat(inventoryTypeList, "dicValue", record.orderStatus) }}</span>
</template>
</a-table-column> -->
</a-table-column>
<a-table-column title="处理状态" data-index="processStatus" align="center">
<template #cell="{ record }">
<span>{{ dictFormat(refundStatusList, "dicValue", record.processStatus) }}</span>

View File

@ -1,3 +1,85 @@
<template>
<div class="snow-page">
<div class="snow-inner">
<s-layout-tools>
<template #right>
<a-space wrap>
<a-button type="primary" @click="onAdd">
<template #icon><icon-plus /></template>
<span>新增</span>
</a-button>
</a-space>
</template>
</s-layout-tools>
<a-table
row-key="id"
:data="moduleList"
:bordered="{ cell: true }"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<template #columns>
<a-table-column title="中控SN码" data-index="ecuSn" align="center"></a-table-column>
<a-table-column title="中控编号" data-index="ecuCode" align="center"></a-table-column>
<a-table-column title="运营商" data-index="operatorId" align="center">
<template #cell="{ record }">
<span>{{ dictFormat(operatorAllList, "operatorId", record.operatorId, "operatorName") }}</span>
</template>
</a-table-column>
<a-table-column title="中控品牌" data-index="ecuBrand" align="center">
<template #cell="{ record }">
<span>{{ dictFormat(ecuBrandList, "dicValue", record.ecuBrand) }}</span>
</template>
</a-table-column>
<a-table-column title="创建时间" data-index="createdAt" :width="180" align="center"></a-table-column>
<!-- TUDO: 暂时不做操作功能 -->
<!-- <a-table-column title="操作" :width="280" align="center" :fixed="'right'">
<template #cell="{ record }">
<a-space>
<a-button type="primary" size="mini" :disabled="record.sysRole">
<template #icon><icon-edit /></template>
<span>修改</span>
</a-button>
</a-space>
</template>
</a-table-column> -->
</template>
</a-table>
</div>
<!-- 模态框 -->
<a-modal v-model:visible="open" @close="afterClose" @before-ok="handleOk" @cancel="afterClose">
<template #title> {{ title }} </template>
<a-form ref="addFormRef" :model="addForm" :rules="rules" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }">
<a-form-item field="ecuCode" label="中控编号" validate-trigger="blur">
<a-input v-model="addForm.ecuCode" placeholder="请输入中控编号" />
</a-form-item>
<a-form-item field="ecuSn" label="中控SN码" validate-trigger="blur">
<a-input v-model="addForm.ecuSn" placeholder="请输入中控SN码" />
</a-form-item>
<a-form-item field="operatorId" label="运营商">
<a-select v-model="addForm.operatorId" placeholder="请选择运营商" validate-trigger="change">
<a-option v-for="it in operatorAllList" :key="it.operatorId" :value="it.operatorId">{{ it.operatorName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="ecuBrand" label="中控品牌">
<a-select v-model="addForm.ecuBrand" placeholder="请选择中控品牌" validate-trigger="change">
<a-option v-for="it in ecuBrandList" :key="it.dicId" :value="it.dicValue">{{ it.dicValueName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="bindBattery" label="是否绑定电池">
<a-radio-group v-model="addForm.bindBattery">
<a-radio :value="true"></a-radio>
<a-radio :value="false"></a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { getOperatorAllListAPI } from "@/api/modules/system";
import { useSystemStore } from "@/store/modules/system";
@ -157,85 +239,5 @@ onBeforeMount(async () => {
await getAllOperatorList();
});
</script>
<template>
<div class="snow-page">
<div class="snow-inner">
<s-layout-tools>
<template #right>
<a-space wrap>
<a-button type="primary" @click="onAdd">
<template #icon><icon-plus /></template>
<span>新增</span>
</a-button>
</a-space>
</template>
</s-layout-tools>
<a-table
row-key="id"
:data="moduleList"
:bordered="{ cell: true }"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<template #columns>
<a-table-column title="中控SN码" data-index="ecuSn" align="center"></a-table-column>
<a-table-column title="中控编号" data-index="ecuCode" align="center"></a-table-column>
<a-table-column title="运营商" data-index="operatorId" align="center">
<template #cell="{ record }">
<span>{{ dictFormat(operatorAllList, "operatorId", record.operatorId, "operatorName") }}</span>
</template>
</a-table-column>
<a-table-column title="中控品牌" data-index="ecuBrand" align="center">
<template #cell="{ record }">
<span>{{ dictFormat(ecuBrandList, "dicValue", record.ecuBrand) }}</span>
</template>
</a-table-column>
<a-table-column title="创建时间" data-index="createdAt" :width="180" align="center"></a-table-column>
<!-- TUDO: 暂时不做操作功能 -->
<!-- <a-table-column title="操作" :width="280" align="center" :fixed="'right'">
<template #cell="{ record }">
<a-space>
<a-button type="primary" size="mini" :disabled="record.sysRole">
<template #icon><icon-edit /></template>
<span>修改</span>
</a-button>
</a-space>
</template>
</a-table-column> -->
</template>
</a-table>
</div>
<!-- 模态框 -->
<a-modal v-model:visible="open" @close="afterClose" @before-ok="handleOk" @cancel="afterClose">
<template #title> {{ title }} </template>
<a-form ref="addFormRef" :model="addForm" :rules="rules" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }">
<a-form-item field="ecuCode" label="中控编号" validate-trigger="blur">
<a-input v-model="addForm.ecuCode" placeholder="请输入中控编号" />
</a-form-item>
<a-form-item field="ecuSn" label="中控SN码" validate-trigger="blur">
<a-input v-model="addForm.ecuSn" placeholder="请输入中控SN码" />
</a-form-item>
<a-form-item field="operatorId" label="运营商">
<a-select v-model="addForm.operatorId" placeholder="请选择运营商" validate-trigger="change">
<a-option v-for="it in operatorAllList" :key="it.operatorId" :value="it.operatorId">{{ it.operatorName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="ecuBrand" label="中控品牌">
<a-select v-model="addForm.ecuBrand" placeholder="请选择中控品牌" validate-trigger="change">
<a-option v-for="it in ecuBrandList" :key="it.dicId" :value="it.dicValue">{{ it.dicValueName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="bindBattery" label="是否绑定电池">
<a-radio-group v-model="addForm.bindBattery">
<a-radio :value="true"></a-radio>
<a-radio :value="false"></a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<style lang="scss" scoped></style>

View File

@ -1,162 +0,0 @@
<script setup lang="ts">
import { getOperatorAllListAPI } from "@/api/modules/system";
import { addOrUpdateOperatorConfigAPI, getOperatorConfigByOperatorIdAPI } from "@/api/modules/serviceConfiguration";
import { OperatorConfigFormType } from "@/api/modules/serviceConfiguration/types";
import { deepClone } from "@/utils";
const operatorAllList = ref<any>([]);
const addFormRef = ref<any>(null);
const addForm = ref<OperatorConfigFormType>({
operatorId: "",
dispatchDuration: undefined,
rideDuration: undefined,
inspectionIntervalDuration: undefined,
operatorPhone: ""
});
const rules = {
operatorId: [{ required: true, message: "请选择运营商", trigger: "change" }],
dispatchDuration: [{ required: true, message: "请输入配置项时长", trigger: "change" }],
rideDuration: [{ required: true, message: "请输入配置项骑行时长", trigger: "change" }],
inspectionIntervalDuration: [{ required: true, message: "请输入配置项时长", trigger: "change" }],
operatorPhone: [{ required: true, message: "请输入运营商手机号", trigger: "change" }]
};
const isLoading = ref<boolean>(false);
//
const getAllOperatorList = async () => {
return new Promise<void>(async resolve => {
try {
const res: any = await getOperatorAllListAPI();
if (res.code === 200) {
operatorAllList.value = res.data;
resolve();
}
} catch (error) {
console.error("获取运营商列表失败:", error);
resolve();
}
});
};
//
async function submit() {
let state = await addFormRef.value.validate();
if (state) return false; //
isLoading.value = true;
const params = deepClone(addForm.value);
try {
const res: any = await addOrUpdateOperatorConfigAPI(params);
arcoMessage("success", "保存成功");
} catch (error) {
console.error("保存失败:", error);
} finally {
isLoading.value = false;
}
}
watch(
() => addForm.value.operatorId,
async newOperatorId => {
if (newOperatorId) {
try {
const { data }: any = await getOperatorConfigByOperatorIdAPI(newOperatorId);
if (data) {
addForm.value = { ...addForm.value, ...data };
} else {
addForm.value = {
operatorId: newOperatorId,
dispatchDuration: undefined,
rideDuration: undefined,
inspectionIntervalDuration: undefined,
operatorPhone: ""
};
}
} catch (error) {
console.error("获取运营商配置失败:", error);
}
}
}
);
onMounted(async () => {
await getAllOperatorList();
addForm.value.operatorId = operatorAllList.value.length > 0 ? operatorAllList.value[0].operatorId : "";
});
</script>
<template>
<div class="snow-page">
<div class="snow-inner">
<div class="header">运营商配置</div>
<div class="form_bar">
<a-form ref="addFormRef" :model="addForm" :rules="rules">
<a-form-item field="operatorId" label="运营商:" validate-trigger="blur">
<a-select v-model="addForm.operatorId" :style="{ width: '320px' }" placeholder="请选择">
<a-option v-for="it in operatorAllList" :key="it.operatorId" :value="it.operatorId">{{ it.operatorName }}</a-option>
</a-select>
</a-form-item>
<a-form-item
field="dispatchDuration"
label="时长配置(h):"
validate-trigger="blur"
tooltip="车辆多长时间无单生成调度工单"
>
<a-input-number
v-model="addForm.dispatchDuration"
:style="{ width: '320px' }"
placeholder="请输入配置项时长"
:min="0"
/>
</a-form-item>
<a-form-item
field="rideDuration"
label="骑行时长配置(h):"
validate-trigger="blur"
tooltip="调度完成后多长时间内骑行是有效的"
>
<a-input-number
v-model="addForm.rideDuration"
:style="{ width: '320px' }"
placeholder="请输入骑行时长配置"
:min="0"
/>
</a-form-item>
<a-form-item field="inspectionIntervalDuration" label="巡检间隔时长(日):" validate-trigger="blur">
<a-input-number
v-model="addForm.inspectionIntervalDuration"
:style="{ width: '320px' }"
placeholder="请输入巡检间隔时长"
:min="0"
/>
</a-form-item>
<a-form-item field="operatorPhone" label="联系电话:" validate-trigger="blur">
<a-input :style="{ width: '320px' }" placeholder="请输入联系电话" allow-clear v-model="addForm.operatorPhone" />
</a-form-item>
</a-form>
</div>
<div class="footer">
<a-button type="primary" :loading="isLoading" @click="submit">{{ isLoading ? "正在保存中..." : "保存" }}</a-button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.header {
width: 100%;
font-size: 22px;
font-weight: bold;
text-align: center;
}
.footer {
width: 100%;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 50px;
}
.form_bar {
margin-top: 20px;
}
</style>

View File

@ -405,8 +405,7 @@ const onDetailUpdate = (record: any) => {
deatilForm.value.dicValueId = record.dicValueId;
deatilForm.value.dicValueName = record.dicValueName;
deatilForm.value.dicValue = record.dicValue;
deatilForm.value.dicIcon = record.dicIcon;
deatilForm.value.dicId = record.dicId;
deatilForm.value.dicId = recordData.value.dicId;
};
/**

View File

@ -16,10 +16,10 @@ const form = ref({
const pagination = ref({
current: 1,
total: 0,
pageSize: 10,
showPageSize: true,
showTotal: true
showTotal: true,
total: 0
});
/**
@ -100,17 +100,9 @@ const onAdd = () => {
fileList.value = [];
};
const search = () => {
pagination.value.current = 1;
getImageList();
};
const search = () => {};
const reset = () => {
form.value = {
imgName: ""
};
search();
};
const reset = () => {};
//
const onDelete = async (record: any) => {
@ -170,16 +162,6 @@ const handleOk = async () => {
}
};
//
async function copyFileUrl(record: any) {
const { fileUrl } = record;
console.log(navigator.clipboard);
if (navigator.clipboard) {
const res: any = await navigator.clipboard.writeText(fileUrl);
arcoMessage("success", "复制成功");
}
}
async function getImageList() {
loading.value = true;
try {
@ -204,17 +186,6 @@ async function getImageList() {
}
}
const handlePageSizeChange = (pageSize: number) => {
pagination.value.pageSize = pageSize;
pagination.value.current = 1;
getImageList();
};
const handlePageChange = (page: number) => {
pagination.value.current = page;
getImageList();
};
onMounted(() => {
getImageList();
});
@ -253,10 +224,6 @@ onMounted(() => {
:bordered="{ cell: true }"
row-key="id"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<template #columns>
<a-table-column align="center" title="图片名称" data-index="imgName" :ellipsis="true" :tooltip="true"></a-table-column>
@ -270,11 +237,7 @@ onMounted(() => {
/>
</template>
</a-table-column>
<a-table-column title="图片后缀" align="center" data-index="fileUrl" :ellipsis="true" :tooltip="true">
<template #cell="{ record }">
<a-link @click="copyFileUrl(record)">{{ record.fileUrl }}</a-link>
</template>
</a-table-column>
<a-table-column title="图片后缀" align="center" data-index="fileUrl" :ellipsis="true" :tooltip="true"></a-table-column>
<a-table-column
title="创建时间"
align="center"
@ -323,7 +286,7 @@ onMounted(() => {
Authorization: `${token}`
}"
:data="{
imgCode: addForm.imgCode ? addForm.imgCode : ''
fileType: 'ebikeImg'
}"
@error="fileError"
:on-before-remove="deleteFile"
@ -334,6 +297,4 @@ onMounted(() => {
</div>
</template>
<style lang="scss" scoped>
//
</style>
<style lang="scss" scoped></style>

View File

@ -1,208 +0,0 @@
<script setup lang="ts">
import { onlineUpgradeAPI } from "@/api/modules/system";
import { deepClone, dictTranslate } from "@/utils";
import { useSystemStore } from "@/store/modules/system";
import { useUserInfoStore } from "@/store/modules/user-info";
import axios from "axios";
const { VITE_APP_BASE_URL, MODE } = import.meta.env;
//
const remoteUpgradeUrl =
MODE === "development" ? "/api/operations/ebikeEcuInfo/upload" : VITE_APP_BASE_URL + "/operations/ebikeEcuInfo/upload";
const system = useSystemStore();
const { token } = useUserInfoStore();
const addFormRef = ref<any>(null);
const fileList = ref<any[]>([]);
const isLoading = ref<boolean>(false);
const addForm = ref<any>({
fullUpgrade: false,
ecuSn: "",
bikeCode: "",
ecuBrand: "",
type: "",
url: ""
});
const options = [
{ label: "是", value: true },
{ label: "否", value: false }
];
const ecuBrandList = ref<any[]>([]);
const typeList = ref<any[]>([]);
const rules = {
fullUpgrade: [{ required: true, message: "请选择是否全部升级", trigger: "change", type: "boolean" }],
ecuBrand: [{ required: true, message: "请选择中控品牌", trigger: "change" }],
type: [{ required: true, message: "请选择升级类型", trigger: "change" }],
url: [{ required: true, message: "请上传文件地址", trigger: "change" }]
};
function fileSuccess(fileItem: any) {
const { response } = fileItem;
// addForm.value.url = response.data;
console.log("response:", response.data);
}
function fileError(err: any) {
console.error("err:", err);
}
//
async function customRequest(option: any) {
const { onProgress, onError, onSuccess, fileItem, name } = option;
const formData = new FormData();
formData.append(name || "file", fileItem.file);
try {
const response = await axios.post(remoteUpgradeUrl, formData, {
headers: {
Authorization: token,
"Content-Type": "multipart/form-data"
},
onUploadProgress: (progressEvent: any) => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
onProgress(percentCompleted, progressEvent);
},
timeout: 60000 //
});
if (response.status === 200) {
onSuccess(response.data, fileItem);
addForm.value.url = response.data.data.fileUrl;
} else {
onError(new Error("上传失败"), fileItem);
}
} catch (error) {
console.error("上传失败:", error);
} finally {
}
}
function resetForm() {
addForm.value = {
fullUpgrade: false,
ecuSn: "",
bikeCode: "",
ecuBrand: "",
type: "",
url: ""
};
fileList.value = [];
if (addFormRef.value) {
addFormRef.value.resetFields();
}
}
//
async function submit() {
if (fileList.value.length > 0) {
const file = fileList.value[0];
addForm.value.url = file.response.data;
}
let state = await addFormRef.value.validate();
if (state) return false; //
isLoading.value = true;
try {
const res: any = await onlineUpgradeAPI(deepClone(addForm.value));
if (res) {
arcoMessage("success", "升级成功,请耐心等待设备升级完成");
resetForm();
} else {
arcoMessage("error", "升级失败");
}
} catch (error: any) {
arcoMessage("error", `${error.message} || '升级失败'`);
console.error("升级失败:", error);
} finally {
isLoading.value = false;
}
}
onBeforeMount(async () => {
ecuBrandList.value = deepClone(system.getDictByCode("ecuBrandCode"));
typeList.value = deepClone(system.getDictByCode("ECU_UPGRADE"));
});
</script>
<template>
<div class="snow-page">
<div class="snow-inner">
<div class="header">模块在线升级</div>
<div class="form_bar">
<a-form ref="addFormRef" :model="addForm" :rules="rules">
<a-form-item field="fullUpgrade" label="是否全部升级:" validate-trigger="blur">
<a-radio-group v-model="addForm.fullUpgrade" :options="options" />
</a-form-item>
<a-form-item v-if="!addForm.fullUpgrade" field="bikeCode" label="车辆编号:" validate-trigger="blur">
<a-input :style="{ width: '320px' }" placeholder="请输入车辆编号" allow-clear v-model="addForm.bikeCode" />
</a-form-item>
<a-form-item v-if="!addForm.fullUpgrade" field="ecuSn" label="中控编号:" validate-trigger="blur">
<a-input :style="{ width: '320px' }" placeholder="请输入中控编号" allow-clear v-model="addForm.ecuSn" />
</a-form-item>
<a-form-item field="ecuBrand" label="中控品牌:" validate-trigger="blur">
<a-select v-model="addForm.ecuBrand" :style="{ width: '320px' }" placeholder="请选择">
<a-option v-for="it in ecuBrandList" :key="it.dicId" :value="it.dicValue">{{ it.dicValueName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="type" label="升级类型:" validate-trigger="blur">
<a-select v-model="addForm.type" :style="{ width: '320px' }" placeholder="请选择" allow-search>
<a-option v-for="it in typeList" :key="it.dicId" :value="it.dicValue">{{ it.dicValueName }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="url" label="上传升级文件:" validate-trigger="blur">
<a-upload
v-model:file-list="fileList"
name="file"
:custom-request="customRequest"
:limit="1"
@success="fileSuccess"
@error="fileError"
>
<template #upload-button>
<div
style="
background-color: var(--color-fill-2);
color: var(--color-text-1);
border: 1px dashed var(--color-fill-4);
height: 158px;
width: 380px;
border-radius: 2;
line-height: 158px;
text-align: center;
"
>
<div>
拖动文件
<span style="color: #3370ff"> 点击上传</span>
</div>
</div>
</template>
</a-upload>
</a-form-item>
</a-form>
</div>
<div class="footer">
<a-button type="primary" :loading="isLoading" @click="submit">{{ isLoading ? "正在升级中" : "升级设备" }}</a-button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.header {
width: 100%;
font-size: 22px;
font-weight: bold;
text-align: center;
}
.footer {
width: 100%;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 50px;
}
.form_bar {
margin-top: 20px;
}
</style>

View File

@ -249,6 +249,7 @@ const onUpdate = async (record: any) => {
try {
let res: any = await getStaffByIdAPI(record.staffId);
if (res.code === 200) {
console.log(filterIds(res.data.roles, "roleId"));
addForm.value = {
staffId: res.data.staffId,
username: res.data.username,

View File

@ -235,9 +235,7 @@ const handleUpdate = (record: any) => {
open.value = true;
title.value = "修改运营区";
const data = deepClone(record);
if (data.regionPolygon) {
data.coordinates = PointDataHandler.restoreForDisplay(data.regionPolygon.coordinates);
}
data.coordinates = PointDataHandler.restoreForDisplay(data.regionPolygon.coordinates);
AddFormRef.value.updateForm(data);
};

View File

@ -124,9 +124,9 @@ const handleUpdate = (record: any) => {
};
const handleDelete = async (record: any) => {
const { siteId } = record;
const { regionId } = record;
try {
const res: any = await deleteSiteAPI(siteId);
const res: any = await deleteSiteAPI(regionId);
if (res.code === 200) {
arcoMessage("success", "删除成功");
getRegionPage();

View File

@ -28,7 +28,6 @@ export default defineConfig(({ mode }) => {
rewrite: path => path.replace(/^\/api/, ""),
bypass(req, res, options) {
// @ts-ignore
// console.log(res, options);
const proxyUrl = new URL(options.rewrite(req.url) || "", options.target)?.href || "";
console.log(proxyUrl);
req.headers["x-req-proxyUrl"] = proxyUrl;