Squashed commit of the following:
All checks were successful
local-test分支构建发布 / Docker-Build (push) Successful in 2m36s
All checks were successful
local-test分支构建发布 / Docker-Build (push) Successful in 2m36s
commit6296877798
Author: joylink_zhaoerwei <Bob_Engineer@163.com> Date: Fri Sep 6 11:08:37 2024 +0800 权限代码备用 commitbe4df53219
Author: joylink_zhaoerwei <Bob_Engineer@163.com> Date: Mon Jul 15 16:10:30 2024 +0800 同步北京部分计轴 commit20d39f9f4a
Author: joylink_zhaoerwei <Bob_Engineer@163.com> Date: Mon Jul 15 15:41:09 2024 +0800 同步区段复制控制-A键直接复制到端点 commitd345a1dc87
Author: joylink_zhaoerwei <Bob_Engineer@163.com> Date: Mon Jul 15 15:17:30 2024 +0800 前端框架版本升级+同步北京区段道岔移动时端点吸附
This commit is contained in:
parent
45b34cb3b9
commit
076b366137
2
.env.dev
2
.env.dev
@ -1,4 +1,4 @@
|
||||
API=192.168.3.233:9081
|
||||
API=192.168.33.233:9081
|
||||
HTTP=http://
|
||||
NS=
|
||||
WS=ws://
|
||||
|
@ -23,7 +23,7 @@
|
||||
"centrifuge": "^4.0.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"google-protobuf": "^3.21.2",
|
||||
"jl-graphic": "git+https://gitea.joylink.club/joylink/graphic-pixi.git#v0.1.3",
|
||||
"jl-graphic": "git+https://gitea.joylink.club/joylink/graphic-pixi.git#v0.1.15",
|
||||
"js-base64": "^3.7.5",
|
||||
"pinia": "^2.0.11",
|
||||
"quasar": "^2.6.0",
|
||||
|
150
src/api/AuthApi.ts
Normal file
150
src/api/AuthApi.ts
Normal file
@ -0,0 +1,150 @@
|
||||
import { api } from 'src/boot/axios';
|
||||
import { PageDto, PageQueryDto } from './ApiCommon';
|
||||
|
||||
const AuthBase = '/api/role';
|
||||
|
||||
export interface Role {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class PagingQueryParams extends PageQueryDto {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface createRoleParams {
|
||||
name: string;
|
||||
resList: number[];
|
||||
}
|
||||
|
||||
export interface editRoleParams extends createRoleParams {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface RoleInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
paths: PathItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询角色信息
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function pageQueryRole(
|
||||
params: PagingQueryParams
|
||||
): Promise<PageDto<Role>> {
|
||||
const response = await api.get(`${AuthBase}/role/page`, {
|
||||
params: params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建角色
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function createRole(data: createRoleParams) {
|
||||
return api.post(`${AuthBase}/role/saveOrUpdate`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
* @param id 角色id
|
||||
*/
|
||||
export function deleteRole(id: number) {
|
||||
return api.delete(`${AuthBase}/role/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改角色数据
|
||||
* @param id 角色id
|
||||
* @param data
|
||||
*/
|
||||
export function saveRoleData(data: editRoleParams) {
|
||||
return api.post(`${AuthBase}/role/saveOrUpdate`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色详情
|
||||
* @param id 角色id
|
||||
* @returns
|
||||
*/
|
||||
export async function getRoleInfo(id: number): Promise<RoleInfo> {
|
||||
const response = await api.get(`${AuthBase}/role/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
interface LinkRole {
|
||||
id: number;
|
||||
roleList: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户关联角色
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function userLinkRole(params: LinkRole): Promise<string> {
|
||||
const response = await api.post('api/user/edit', params);
|
||||
return response.data.token;
|
||||
}
|
||||
|
||||
export interface PathItem {
|
||||
id: number;
|
||||
method: string;
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询接口信息
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function pageQueryPath(
|
||||
params: PagingQueryParams
|
||||
): Promise<PageDto<PathItem>> {
|
||||
const response = await api.get(`${AuthBase}/res/page`, {
|
||||
params: params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建接口路径
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function createPath(data: Omit<PathItem, 'id'>) {
|
||||
return api.post(`${AuthBase}/res/saveOrUpdate`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除接口路径
|
||||
* @param id 接口路径id
|
||||
*/
|
||||
export function deletePath(id: number) {
|
||||
return api.delete(`${AuthBase}/res/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改接口路径数据
|
||||
* @param id 接口路径id
|
||||
* @param data
|
||||
*/
|
||||
export function savePathData(data: PathItem) {
|
||||
return api.post(`${AuthBase}/res/saveOrUpdate`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取接口路径数据详情
|
||||
* @param id 接口路径id
|
||||
* @returns
|
||||
*/
|
||||
export async function getPathInfo(id: number): Promise<PathItem> {
|
||||
const response = await api.get(`${AuthBase}/res/${id}`);
|
||||
return response.data;
|
||||
}
|
@ -10,12 +10,13 @@ interface RegisterInfo {
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface User {
|
||||
export interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
mobile: string;
|
||||
password: string;
|
||||
registerTime: string;
|
||||
roleList: { roleId: number; roleName: string }[];
|
||||
}
|
||||
|
||||
const PasswordSult = '4a6d74126bfd06d69406fcccb7e7d5d9'; // 密码加盐
|
||||
|
6
src/components/AuthData.ts
Normal file
6
src/components/AuthData.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export enum MethodType {
|
||||
POST = 'POST',
|
||||
PUT = 'PUT',
|
||||
DELETE = 'DELETE',
|
||||
GET = 'GET',
|
||||
}
|
@ -99,6 +99,16 @@ const list = reactive([
|
||||
label: '用户管理',
|
||||
icon: 'manage_accounts',
|
||||
},
|
||||
{
|
||||
path: '/sysManage/role',
|
||||
label: '权限管理',
|
||||
icon: 'nature_people',
|
||||
},
|
||||
{
|
||||
path: '/sysManage/authPath',
|
||||
label: '权限接口管理',
|
||||
icon: 'menu_open',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
@ -14,6 +14,9 @@ import {
|
||||
IGraphicStorage,
|
||||
ContextMenu,
|
||||
MenuItemOptions,
|
||||
calculateDistanceFromPointToLine,
|
||||
distance2,
|
||||
getRectangleCenter,
|
||||
} from 'jl-graphic';
|
||||
import { IscsFanData } from './graphics/IscsFanInteraction';
|
||||
import { LinkData } from './graphics/LinkInteraction';
|
||||
@ -296,6 +299,99 @@ export function initDrawApp(): IDrawApp {
|
||||
},
|
||||
})
|
||||
);
|
||||
// KeyA 用于区段复制--控制生成的区段位置
|
||||
const graphicCopyPlugin = app.app.graphicCopyPlugin;
|
||||
const copySectionListener = new KeyListener({
|
||||
value: 'KeyA',
|
||||
global: true,
|
||||
onPress: () => {
|
||||
graphicCopyPlugin.updateMoveLimit('sectionPointLimit');
|
||||
},
|
||||
});
|
||||
graphicCopyPlugin.addGraphicControlers([
|
||||
{
|
||||
controlerList: [copySectionListener],
|
||||
check: () => {
|
||||
if (
|
||||
graphicCopyPlugin.copys.length == 1 &&
|
||||
graphicCopyPlugin.copys[0].type == Section.Type
|
||||
)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
moveLimitOption: {
|
||||
moveLimitName: 'sectionPointLimit',
|
||||
moveLimit: (e) => {
|
||||
const mousePos = app.toCanvasCoordinates(e.global);
|
||||
const selectSection = app.selectedGraphics[0] as Section;
|
||||
let selectSectionLeft = selectSection.localToCanvasPoint(
|
||||
selectSection.getStartPoint()
|
||||
);
|
||||
let selectSectionRight = selectSection.localToCanvasPoint(
|
||||
selectSection.getEndPoint()
|
||||
);
|
||||
[selectSectionLeft, selectSectionRight] =
|
||||
selectSectionLeft.x < selectSectionRight.x
|
||||
? [selectSectionLeft, selectSectionRight]
|
||||
: [selectSectionRight, selectSectionLeft];
|
||||
//要移动到目标位的区段
|
||||
const sections = app.queryStore.queryByType<Section>(Section.Type);
|
||||
const minDistanceSection = sections.reduce((prev, cur) => {
|
||||
const prevDistance = calculateDistanceFromPointToLine(
|
||||
prev.localToCanvasPoint(prev.getStartPoint()),
|
||||
prev.localToCanvasPoint(prev.getEndPoint()),
|
||||
mousePos
|
||||
);
|
||||
const curDistance = calculateDistanceFromPointToLine(
|
||||
cur.localToCanvasPoint(cur.getStartPoint()),
|
||||
cur.localToCanvasPoint(cur.getEndPoint()),
|
||||
mousePos
|
||||
);
|
||||
return prevDistance > curDistance ||
|
||||
(prevDistance == curDistance &&
|
||||
distance2(
|
||||
prev.localToCanvasPoint(prev.getStartPoint()),
|
||||
mousePos
|
||||
) >
|
||||
distance2(
|
||||
cur.localToCanvasPoint(cur.getStartPoint()),
|
||||
mousePos
|
||||
))
|
||||
? cur
|
||||
: prev;
|
||||
});
|
||||
const minDistanceRefSectionsPos =
|
||||
minDistanceSection.localToCanvasPoint(
|
||||
getRectangleCenter(
|
||||
minDistanceSection.lineGraphic.getLocalBounds()
|
||||
)
|
||||
);
|
||||
let minDistanceSectionLeft = minDistanceSection.localToCanvasPoint(
|
||||
minDistanceSection.getStartPoint()
|
||||
);
|
||||
let minDistanceSectionRight = minDistanceSection.localToCanvasPoint(
|
||||
minDistanceSection.getEndPoint()
|
||||
);
|
||||
[minDistanceSectionLeft, minDistanceSectionRight] =
|
||||
minDistanceSectionLeft.x < minDistanceSectionRight.x
|
||||
? [minDistanceSectionLeft, minDistanceSectionRight]
|
||||
: [minDistanceSectionRight, minDistanceSectionLeft];
|
||||
|
||||
if (mousePos.x > minDistanceRefSectionsPos.x) {
|
||||
graphicCopyPlugin.container.position.x =
|
||||
minDistanceSectionRight.x - selectSectionLeft.x;
|
||||
graphicCopyPlugin.container.position.y =
|
||||
minDistanceSectionRight.y - selectSectionLeft.y;
|
||||
} else {
|
||||
graphicCopyPlugin.container.position.x =
|
||||
minDistanceSectionLeft.x - selectSectionRight.x;
|
||||
graphicCopyPlugin.container.position.y =
|
||||
minDistanceSectionLeft.y - selectSectionRight.y;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
return drawApp;
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,12 @@ import {
|
||||
IAxleCountingData,
|
||||
AxleCounting,
|
||||
AxleCountingTemplate,
|
||||
AxleCountingConsts,
|
||||
} from './AxleCounting';
|
||||
import { Section, SectionPort } from '../section/Section';
|
||||
import { Turnout, TurnoutPort } from '../turnout/Turnout';
|
||||
import { IRelatedRefData, createRelatedRefProto } from '../CommonGraphics';
|
||||
import { Signal } from '../signal/Signal';
|
||||
import { graphicData } from 'src/protos/stationLayoutGraphics';
|
||||
|
||||
export interface IAxleCountingDrawOptions {
|
||||
newData: () => IAxleCountingData;
|
||||
@ -112,14 +112,7 @@ export class AxleCountingDraw extends GraphicDrawAssistant<
|
||||
const refData2 = createRelatedRefProto(graphic.type, graphic.id, port);
|
||||
const axleCounting = new AxleCounting(direction);
|
||||
axleCounting.loadData(this.graphicTemplate.datas);
|
||||
if (graphic.type == 'Turnout') {
|
||||
axleCounting.position.set(ps.x, ps.y);
|
||||
} else {
|
||||
axleCounting.position.set(
|
||||
ps.x,
|
||||
ps.y - AxleCountingConsts.offsetSection * direction
|
||||
);
|
||||
}
|
||||
axleCounting.id = GraphicIdGenerator.next();
|
||||
axleCounting.datas.axleCountingRef = [refData2, refData1];
|
||||
axleCounting.datas.code = `${graphic.datas.code}-${port}+${refGraphic.datas.code}-${refPort}`;
|
||||
@ -139,14 +132,7 @@ export class AxleCountingDraw extends GraphicDrawAssistant<
|
||||
const refData = createRelatedRefProto(graphic.type, graphic.id, port);
|
||||
const axleCounting = new AxleCounting(direction);
|
||||
axleCounting.loadData(this.graphicTemplate.datas);
|
||||
if (graphic.type == 'Turnout') {
|
||||
axleCounting.position.set(ps.x, ps.y);
|
||||
} else {
|
||||
axleCounting.position.set(
|
||||
ps.x,
|
||||
ps.y - AxleCountingConsts.offsetSection * direction
|
||||
);
|
||||
}
|
||||
axleCounting.id = GraphicIdGenerator.next();
|
||||
axleCounting.datas.axleCountingRef = [refData];
|
||||
axleCounting.datas.code = `${graphic.datas.code}-${port}`;
|
||||
@ -156,9 +142,28 @@ export class AxleCountingDraw extends GraphicDrawAssistant<
|
||||
}
|
||||
oneGenerates(height: Point) {
|
||||
const map = new Map();
|
||||
const axleCountings = this.app.queryStore.queryByType<AxleCounting>(
|
||||
AxleCounting.Type
|
||||
const needDelete: AxleCounting[] = [];
|
||||
const axleCountings = this.app.queryStore
|
||||
.queryByType<AxleCounting>(AxleCounting.Type)
|
||||
.filter((axleCounting) => {
|
||||
if (axleCounting.datas.axleCountingRef.length == 1) {
|
||||
const refInfo = axleCounting.datas.axleCountingRef[0];
|
||||
if (refInfo.deviceType == graphicData.RelatedRef.DeviceType.Section) {
|
||||
const refSection = this.app.queryStore.queryById<Section>(
|
||||
refInfo.id
|
||||
);
|
||||
if (
|
||||
refSection.datas.paRef != undefined &&
|
||||
refSection.datas.pbRef != undefined
|
||||
) {
|
||||
needDelete.push(axleCounting);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
this.app.deleteGraphics(...needDelete);
|
||||
const axleCountingRefs: IRelatedRefData[] = [];
|
||||
axleCountings.forEach((axleCounting) => {
|
||||
axleCountingRefs.push(...axleCounting.datas.axleCountingRef);
|
||||
|
@ -49,6 +49,7 @@ import { AxleCounting } from '../axleCounting/AxleCounting';
|
||||
import { LogicSectionData } from 'src/drawApp/graphics/LogicSectionInteraction';
|
||||
import { LogicSectionDraw } from '../logicSection/LogicSectionDrawAssistant';
|
||||
import { LogicSection } from '../logicSection/LogicSection';
|
||||
import { buildDragMoveAbsorbablePositions } from '../turnout/TurnoutDrawAssistant';
|
||||
|
||||
export class SectionDraw extends GraphicDrawAssistant<
|
||||
SectionTemplate,
|
||||
@ -443,11 +444,13 @@ export class SectionPointEditPlugin extends GraphicInteractionPlugin<Section> {
|
||||
g.on('selected', this.onSelected, this);
|
||||
g.on('unselected', this.onUnselected, this);
|
||||
g.on('_rightclick', this.onContextMenu, this);
|
||||
g.on('transformstart', this.onDragMove, this);
|
||||
}
|
||||
unbind(g: Section): void {
|
||||
g.off('selected', this.onSelected, this);
|
||||
g.off('unselected', this.onUnselected, this);
|
||||
g.off('_rightclick', this.onContextMenu, this);
|
||||
g.off('transformstart', this.onDragMove, this);
|
||||
}
|
||||
|
||||
onContextMenu(e: FederatedMouseEvent) {
|
||||
@ -585,4 +588,10 @@ export class SectionPointEditPlugin extends GraphicInteractionPlugin<Section> {
|
||||
section.draggable = false;
|
||||
}
|
||||
}
|
||||
onDragMove(e: GraphicTransformEvent) {
|
||||
const section = e.target as Section;
|
||||
this.app.setOptions({
|
||||
absorbablePositions: buildDragMoveAbsorbablePositions(section),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
AbsorbableLine,
|
||||
ContextMenu,
|
||||
MenuItemOptions,
|
||||
distance,
|
||||
} from 'jl-graphic';
|
||||
import {
|
||||
ITurnoutData,
|
||||
@ -178,6 +179,126 @@ function buildAbsorbablePositions(turnout: Turnout): AbsorbablePosition[] {
|
||||
return aps;
|
||||
}
|
||||
|
||||
type dragType = Turnout | Section;
|
||||
class DragMoveAbsorbablePoint extends AbsorbablePoint {
|
||||
moveTarget:
|
||||
| {
|
||||
position: IPointData;
|
||||
portPos: IPointData[];
|
||||
}
|
||||
| undefined;
|
||||
constructor(point: IPointData, absorbRange = 15) {
|
||||
super(point, absorbRange);
|
||||
}
|
||||
tryAbsorb(...dragTargets: dragType[]): void {
|
||||
const dragTarget = dragTargets[0];
|
||||
if (dragTarget instanceof Turnout) {
|
||||
if (this.moveTarget == undefined) {
|
||||
const {
|
||||
pointA: [A],
|
||||
pointB: [B],
|
||||
pointC: [C],
|
||||
} = dragTarget.datas;
|
||||
this.moveTarget = {
|
||||
position: dragTarget.getGlobalPosition(),
|
||||
portPos: [
|
||||
dragTarget.localToCanvasPoint(A),
|
||||
dragTarget.localToCanvasPoint(B),
|
||||
dragTarget.localToCanvasPoint(C),
|
||||
],
|
||||
};
|
||||
}
|
||||
const {
|
||||
pointA: [A],
|
||||
pointB: [B],
|
||||
pointC: [C],
|
||||
} = dragTarget.datas;
|
||||
[A, B, C].forEach((p, i) => {
|
||||
const changePos = dragTarget.localToCanvasPoint(p);
|
||||
if (
|
||||
distance(this._point.x, this._point.y, changePos.x, changePos.y) <
|
||||
this.absorbRange &&
|
||||
this.moveTarget
|
||||
) {
|
||||
dragTarget.updatePositionByCanvasPosition(
|
||||
new Point(
|
||||
this.moveTarget.position.x +
|
||||
this._point.x -
|
||||
this.moveTarget.portPos[i].x,
|
||||
this.moveTarget.position.y +
|
||||
this._point.y -
|
||||
this.moveTarget.portPos[i].y
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.moveTarget == undefined) {
|
||||
this.moveTarget = {
|
||||
position: dragTarget.getGlobalPosition(),
|
||||
portPos: [
|
||||
dragTarget.localToCanvasPoint(dragTarget.getStartPoint()),
|
||||
dragTarget.localToCanvasPoint(dragTarget.getEndPoint()),
|
||||
],
|
||||
};
|
||||
}
|
||||
dragTarget
|
||||
.localToCanvasPoints(...dragTarget.datas.points)
|
||||
.forEach((p, i) => {
|
||||
if (
|
||||
distance(this._point.x, this._point.y, p.x, p.y) <
|
||||
this.absorbRange &&
|
||||
this.moveTarget
|
||||
) {
|
||||
dragTarget.updatePositionByCanvasPosition(
|
||||
new Point(
|
||||
this.moveTarget.position.x +
|
||||
this._point.x -
|
||||
this.moveTarget.portPos[i].x,
|
||||
this.moveTarget.position.y +
|
||||
this._point.y -
|
||||
this.moveTarget.portPos[i].y
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function buildDragMoveAbsorbablePositions(
|
||||
target: dragType
|
||||
): AbsorbablePosition[] {
|
||||
const aps: AbsorbablePosition[] = [];
|
||||
|
||||
const sections = target.queryStore.queryByType<Section>(Section.Type);
|
||||
sections.forEach((section) => {
|
||||
if (section.id !== target.id) {
|
||||
section.localToCanvasPoints(...section.datas.points).forEach((p) => {
|
||||
aps.push(new DragMoveAbsorbablePoint(p)); //区段端点
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const turnouts = target.queryStore.queryByType<Turnout>(Turnout.Type);
|
||||
turnouts.forEach((otherTurnout) => {
|
||||
if (otherTurnout.id !== target.id) {
|
||||
const {
|
||||
pointA: [A],
|
||||
pointB: [B],
|
||||
pointC: [C],
|
||||
} = otherTurnout.datas;
|
||||
[A, B, C].forEach((p) => {
|
||||
aps.push(
|
||||
new DragMoveAbsorbablePoint(otherTurnout.localToCanvasPoint(p)) //道岔端点
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return aps;
|
||||
}
|
||||
|
||||
function onEditPointCreate(turnout: Turnout, dp: DraggablePoint) {
|
||||
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||
if (e.isShift()) {
|
||||
@ -290,6 +411,7 @@ export class TurnoutPointsInteractionPlugin extends GraphicInteractionPlugin<Tur
|
||||
g.transformSave = true;
|
||||
g.on('selected', this.onSelected, this);
|
||||
g.on('unselected', this.onUnSelected, this);
|
||||
g.on('transformstart', this.onDragMove, this);
|
||||
}
|
||||
|
||||
unbind(g: Turnout): void {
|
||||
@ -298,6 +420,7 @@ export class TurnoutPointsInteractionPlugin extends GraphicInteractionPlugin<Tur
|
||||
g.graphics.sections.forEach((sectionGraphic) => {
|
||||
sectionGraphic.off('rightclick');
|
||||
});
|
||||
g.off('transformstart', this.onDragMove, this);
|
||||
}
|
||||
|
||||
onSelected(g: DisplayObject) {
|
||||
@ -327,6 +450,13 @@ export class TurnoutPointsInteractionPlugin extends GraphicInteractionPlugin<Tur
|
||||
filter(...grahpics: JlGraphic[]): Turnout[] | undefined {
|
||||
return grahpics.filter((g) => g.type == Turnout.Type) as Turnout[];
|
||||
}
|
||||
|
||||
onDragMove(e: GraphicTransformEvent) {
|
||||
const turnout = e.target as Turnout;
|
||||
this.app.setOptions({
|
||||
absorbablePositions: buildDragMoveAbsorbablePositions(turnout),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type onTurnoutEditPointCreate = (turnout: Turnout, dp: DraggablePoint) => void;
|
||||
|
375
src/pages/AuthPathManage.vue
Normal file
375
src/pages/AuthPathManage.vue
Normal file
@ -0,0 +1,375 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
ref="tableRef"
|
||||
title="权限接口列表"
|
||||
:style="{ height: tableHeight + 'px' }"
|
||||
class="my-sticky-virtscroll-table"
|
||||
:rows="rows"
|
||||
:columns="columnDefs"
|
||||
row-key="id"
|
||||
v-model:pagination="pagination"
|
||||
:rows-per-page-options="[10, 20, 50, 100]"
|
||||
:loading="loading"
|
||||
:filter="filter"
|
||||
:selection="isRole ? 'multiple' : 'none'"
|
||||
v-model:selected="selected"
|
||||
@update:selected="tableSelected"
|
||||
:selected-rows-label="getSelectedString"
|
||||
binary-state-sort
|
||||
@request="onRequest"
|
||||
>
|
||||
<template v-slot:top-right>
|
||||
<q-input
|
||||
dense
|
||||
debounce="1000"
|
||||
v-model="filter.name"
|
||||
label="名称"
|
||||
></q-input>
|
||||
<q-btn flat round color="primary" icon="search" />
|
||||
<q-btn
|
||||
v-if="!isRole"
|
||||
color="primary"
|
||||
label="新建"
|
||||
@click="editFormShow = true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-slot:header-selection="scope">
|
||||
<q-checkbox v-model="scope.selected" />
|
||||
</template>
|
||||
|
||||
<template v-slot:body-selection="scope">
|
||||
<q-checkbox v-model="scope.selected" />
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-operations="props">
|
||||
<q-td :props="props">
|
||||
<div class="q-gutter-sm row justify-center">
|
||||
<q-btn
|
||||
color="primary"
|
||||
label="编辑"
|
||||
@click="edieAuthData(props.row)"
|
||||
/>
|
||||
<q-btn
|
||||
color="red"
|
||||
:disable="operateDisabled"
|
||||
label="删除"
|
||||
@click="deleteAuthData(props.row)"
|
||||
/>
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
|
||||
<q-dialog
|
||||
v-model="editFormShow"
|
||||
persistent
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<q-card style="width: 300px">
|
||||
<q-card-section>
|
||||
<q-form
|
||||
ref="myForm"
|
||||
@submit="edieAuthPath"
|
||||
@reset="onReset"
|
||||
class="q-gutter-md"
|
||||
>
|
||||
<div class="text-h6">{{ pathInfo.id ? '修改' : '新建' }}</div>
|
||||
<q-input outlined label="名称" v-model="pathInfo.name" />
|
||||
<q-input outlined label="接口" v-model="pathInfo.path" />
|
||||
<q-select
|
||||
outlined
|
||||
v-model="pathInfo.methodList"
|
||||
:options="options"
|
||||
multiple
|
||||
map-options
|
||||
emit-value
|
||||
label="方法"
|
||||
lazy-rules
|
||||
:rules="[(val) => val.length > 0 || '请选择方法!']"
|
||||
></q-select>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn color="primary" label="保存" type="submit" />
|
||||
<q-btn label="取消" type="reset" v-close-popup />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, computed, watch } from 'vue';
|
||||
import { useQuasar, type QTableColumn, QForm } from 'quasar';
|
||||
import {
|
||||
createPath,
|
||||
deletePath,
|
||||
getPathInfo,
|
||||
pageQueryPath,
|
||||
PathItem,
|
||||
savePathData,
|
||||
} from '../api/AuthApi';
|
||||
import { successNotify } from '../utils/CommonNotify';
|
||||
import { ApiError } from 'src/boot/axios';
|
||||
import { MethodType } from 'src/components/AuthData';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const $q = useQuasar();
|
||||
|
||||
interface SelectItem {
|
||||
id: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
sizeHeight: number;
|
||||
selects?: PathItem[];
|
||||
}>(),
|
||||
{ sizeHeight: 500, selects: () => [] }
|
||||
);
|
||||
const tableHeight = computed(() => {
|
||||
return props.sizeHeight - 32;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
tableRef.value.requestServerInteraction();
|
||||
selected.value = props.selects;
|
||||
});
|
||||
|
||||
const columnDefs: QTableColumn[] = [
|
||||
{ name: 'id', label: 'ID', field: 'id', align: 'center' },
|
||||
{
|
||||
name: 'name',
|
||||
label: '权限接口名称',
|
||||
field: 'name',
|
||||
required: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'path',
|
||||
label: '接口路径',
|
||||
field: 'path',
|
||||
required: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'method',
|
||||
label: '方法',
|
||||
field: (row) => {
|
||||
return getMethodName(row.method);
|
||||
},
|
||||
align: 'center',
|
||||
},
|
||||
{ name: 'operations', label: '操作', field: 'operations', align: 'center' },
|
||||
];
|
||||
|
||||
const operateDisabled = ref(false);
|
||||
const tableRef = ref();
|
||||
const rows = reactive([]);
|
||||
const filter = reactive({
|
||||
name: '',
|
||||
});
|
||||
const loading = ref(false);
|
||||
const pagination = ref({
|
||||
sortBy: 'desc',
|
||||
descending: false,
|
||||
page: 1,
|
||||
rowsPerPage: 10,
|
||||
rowsNumber: 10,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
async function onRequest(props: any) {
|
||||
const { page, rowsPerPage, sortBy, descending } = props.pagination;
|
||||
const filter = props.filter;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
let response = await pageQueryPath({
|
||||
name: filter.name,
|
||||
current: page,
|
||||
size: rowsPerPage,
|
||||
});
|
||||
|
||||
pagination.value.rowsNumber = response.total;
|
||||
pagination.value.page = page;
|
||||
pagination.value.rowsPerPage = rowsPerPage;
|
||||
pagination.value.sortBy = sortBy;
|
||||
pagination.value.descending = descending;
|
||||
rows.splice(0, rows.length, ...(response.records as []));
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const editFormShow = ref(false);
|
||||
|
||||
interface PathInfo extends Omit<PathItem, 'id' | 'method'> {
|
||||
id: string;
|
||||
methodList: MethodType[];
|
||||
}
|
||||
const pathInfo = reactive<PathInfo>({
|
||||
id: '',
|
||||
methodList: [],
|
||||
name: '',
|
||||
path: '',
|
||||
});
|
||||
|
||||
const options = computed(() => {
|
||||
const list: { label: string; value: string }[] = [];
|
||||
for (let item in MethodType) {
|
||||
const obj = {
|
||||
label: item,
|
||||
value: MethodType[item as MethodType],
|
||||
};
|
||||
list.push(obj);
|
||||
}
|
||||
return list;
|
||||
});
|
||||
|
||||
//编辑
|
||||
function edieAuthData(row: PathItem) {
|
||||
getPathInfo(row.id).then((res) => {
|
||||
pathInfo.id = res.id + '';
|
||||
pathInfo.name = res.name;
|
||||
pathInfo.path = res.path;
|
||||
let list: MethodType[] = [];
|
||||
if (res.method == '*') {
|
||||
list = options.value.map((item) => item.value as MethodType);
|
||||
} else {
|
||||
list = res.method.split(',') as MethodType[];
|
||||
}
|
||||
pathInfo.methodList = list;
|
||||
editFormShow.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
const myForm = ref<QForm | null>(null);
|
||||
|
||||
//提交编辑
|
||||
async function edieAuthPath() {
|
||||
myForm.value?.validate().then(async (res) => {
|
||||
if (res) {
|
||||
operateDisabled.value = true;
|
||||
try {
|
||||
let method = pathInfo.methodList.join(',');
|
||||
const everyM = options.value.every((item) => {
|
||||
return pathInfo.methodList.includes(item.value as MethodType);
|
||||
});
|
||||
if (everyM) {
|
||||
method = '*';
|
||||
}
|
||||
const params = {
|
||||
name: pathInfo.name,
|
||||
path: pathInfo.path,
|
||||
method: method,
|
||||
};
|
||||
if (pathInfo.id) {
|
||||
const cloneParams = Object.assign(params, { id: +pathInfo.id });
|
||||
await savePathData(cloneParams);
|
||||
} else {
|
||||
await createPath(params);
|
||||
}
|
||||
onReset();
|
||||
tableRef.value.requestServerInteraction(); // 刷新列表
|
||||
editFormShow.value = false;
|
||||
successNotify('保存成功!');
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
} finally {
|
||||
operateDisabled.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAuthData(row: PathItem) {
|
||||
operateDisabled.value = true;
|
||||
$q.dialog({
|
||||
title: '确认',
|
||||
message: `确认删除权限接口 "${row.name}" 吗?`,
|
||||
cancel: true,
|
||||
})
|
||||
.onOk(async () => {
|
||||
try {
|
||||
await deletePath(row.id);
|
||||
tableRef.value.requestServerInteraction(); // 刷新列表
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
}
|
||||
})
|
||||
.onDismiss(() => {
|
||||
operateDisabled.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
pathInfo.id = '';
|
||||
pathInfo.name = '';
|
||||
pathInfo.methodList = [];
|
||||
pathInfo.path = '';
|
||||
myForm.value?.resetValidation();
|
||||
}
|
||||
|
||||
function getMethodName(val: string) {
|
||||
let name = val;
|
||||
if (val == '*') {
|
||||
const list = options.value.map((item) => item.value);
|
||||
name = list.join(',');
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
const isRole = computed(() => {
|
||||
// 角色管理
|
||||
return route.path.includes('/sysManage/role');
|
||||
});
|
||||
const selected = ref<SelectItem[]>([]);
|
||||
function getSelectedString() {
|
||||
const nameArr = selected.value.map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
const name = nameArr.join(',');
|
||||
return `已选ID:${name}`;
|
||||
}
|
||||
if (isRole.value) {
|
||||
const index = columnDefs.findIndex((item) => {
|
||||
return item.name == 'operations';
|
||||
});
|
||||
if (index >= 0) {
|
||||
columnDefs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const emit = defineEmits(['selectsed']);
|
||||
function tableSelected() {
|
||||
emit('selectsed', selected.value);
|
||||
}
|
||||
watch(
|
||||
() => props.selects,
|
||||
(val) => {
|
||||
selected.value = val.map((item) => {
|
||||
return { id: item.id };
|
||||
});
|
||||
}
|
||||
);
|
||||
</script>
|
276
src/pages/RoleManage.vue
Normal file
276
src/pages/RoleManage.vue
Normal file
@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
ref="tableRef"
|
||||
title="角色列表"
|
||||
:style="{ height: tableHeight + 'px' }"
|
||||
class="my-sticky-virtscroll-table"
|
||||
:rows="rows"
|
||||
:columns="columnDefs"
|
||||
row-key="id"
|
||||
v-model:pagination="pagination"
|
||||
:rows-per-page-options="[10, 20, 50, 100]"
|
||||
:loading="loading"
|
||||
:filter="filter"
|
||||
binary-state-sort
|
||||
@request="onRequest"
|
||||
>
|
||||
<template v-slot:top-right>
|
||||
<q-input
|
||||
dense
|
||||
debounce="1000"
|
||||
v-model="filter.name"
|
||||
label="名称"
|
||||
></q-input>
|
||||
<q-btn flat round color="primary" icon="search" />
|
||||
<q-btn color="primary" label="新建" @click="editFormShow = true" />
|
||||
</template>
|
||||
|
||||
<template v-slot:body-cell-operations="props">
|
||||
<q-td :props="props">
|
||||
<div class="q-gutter-sm row justify-center">
|
||||
<q-btn
|
||||
color="primary"
|
||||
label="编辑"
|
||||
@click="edieRoleData(props.row)"
|
||||
:disable="props.row.id == 1"
|
||||
/>
|
||||
<q-btn
|
||||
color="red"
|
||||
:disable="operateDisabled || [1, 2].includes(props.row.id)"
|
||||
label="删除"
|
||||
@click="deleteRoleData(props.row)"
|
||||
/>
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
|
||||
<q-dialog
|
||||
v-model="editFormShow"
|
||||
persistent
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<q-card style="width: 1400px; max-width: 80vw">
|
||||
<q-form
|
||||
ref="myForm"
|
||||
@submit="edieRolePath"
|
||||
@reset="onReset"
|
||||
class="q-gutter-md"
|
||||
>
|
||||
<q-card-section>
|
||||
<div class="text-h5">{{ roleInfo.id ? '编辑' : '新建' }}</div>
|
||||
<div class="q-pa-md">
|
||||
<q-input
|
||||
outlined
|
||||
dense
|
||||
label="角色名称"
|
||||
v-model="roleInfo.name"
|
||||
lazy-rules
|
||||
:rules="[(val) => val.length > 0 || '请输入角色名称!']"
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
<AuthPathManage
|
||||
:sizeHeight="600"
|
||||
:selects="roleInfo.paths || []"
|
||||
@selectsed="pathSelectsed"
|
||||
/>
|
||||
<q-card-actions align="right">
|
||||
<q-btn color="primary" label="保存" type="submit" />
|
||||
<q-btn label="取消" type="reset" v-close-popup />
|
||||
</q-card-actions>
|
||||
</q-card-section>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { useQuasar, type QTableColumn, QForm } from 'quasar';
|
||||
import {
|
||||
PathItem,
|
||||
RoleInfo,
|
||||
createRole,
|
||||
createRoleParams,
|
||||
deleteRole,
|
||||
getRoleInfo,
|
||||
pageQueryRole,
|
||||
saveRoleData,
|
||||
} from 'src/api/AuthApi';
|
||||
import { ApiError } from 'src/boot/axios';
|
||||
import { successNotify } from 'src/utils/CommonNotify';
|
||||
import AuthPathManage from './AuthPathManage.vue';
|
||||
const $q = useQuasar();
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
sizeHeight: number;
|
||||
}>(),
|
||||
{ sizeHeight: 500 }
|
||||
);
|
||||
const tableHeight = computed(() => {
|
||||
return props.sizeHeight - 32;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
tableRef.value.requestServerInteraction();
|
||||
});
|
||||
|
||||
const columnDefs: QTableColumn[] = [
|
||||
{ name: 'id', label: '角色ID', field: 'id', align: 'center' },
|
||||
{
|
||||
name: 'name',
|
||||
label: '角色名称',
|
||||
field: 'name',
|
||||
required: true,
|
||||
align: 'center',
|
||||
},
|
||||
{ name: 'operations', label: '操作', field: 'operations', align: 'center' },
|
||||
];
|
||||
|
||||
const operateDisabled = ref(false);
|
||||
|
||||
const tableRef = ref();
|
||||
const rows = reactive([]);
|
||||
const filter = reactive({
|
||||
name: '',
|
||||
});
|
||||
const loading = ref(false);
|
||||
const pagination = ref({
|
||||
sortBy: 'desc',
|
||||
descending: false,
|
||||
page: 1,
|
||||
rowsPerPage: 10,
|
||||
rowsNumber: 10,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
async function onRequest(props: any) {
|
||||
const { page, rowsPerPage, sortBy, descending } = props.pagination;
|
||||
const filter = props.filter;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
let response = await pageQueryRole({
|
||||
name: filter.name,
|
||||
current: page,
|
||||
size: rowsPerPage,
|
||||
});
|
||||
|
||||
pagination.value.rowsNumber = response.total;
|
||||
pagination.value.page = page;
|
||||
pagination.value.rowsPerPage = rowsPerPage;
|
||||
pagination.value.sortBy = sortBy;
|
||||
pagination.value.descending = descending;
|
||||
rows.splice(0, rows.length, ...(response.records as []));
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const editFormShow = ref(false);
|
||||
|
||||
interface RoleItemInfo extends Omit<RoleInfo, 'id'> {
|
||||
id: string;
|
||||
editPaths: number[];
|
||||
}
|
||||
const roleInfo = reactive<RoleItemInfo>({
|
||||
id: '',
|
||||
editPaths: [],
|
||||
name: '',
|
||||
paths: [],
|
||||
});
|
||||
|
||||
//编辑
|
||||
function edieRoleData(row: RoleInfo) {
|
||||
getRoleInfo(row.id).then((res) => {
|
||||
roleInfo.id = res.id + '';
|
||||
roleInfo.name = res.name;
|
||||
const list = res.paths || [];
|
||||
roleInfo.paths = list;
|
||||
roleInfo.editPaths = list.map((item) => item.id);
|
||||
editFormShow.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
const myForm = ref<QForm | null>(null);
|
||||
|
||||
//提交编辑
|
||||
async function edieRolePath() {
|
||||
myForm.value?.validate().then(async (res) => {
|
||||
if (res) {
|
||||
operateDisabled.value = true;
|
||||
try {
|
||||
const params: createRoleParams = {
|
||||
name: roleInfo.name,
|
||||
resList: roleInfo.editPaths,
|
||||
};
|
||||
if (roleInfo.id) {
|
||||
const cloneParams = Object.assign(params, { id: +roleInfo.id });
|
||||
await saveRoleData(cloneParams);
|
||||
} else {
|
||||
await createRole(params);
|
||||
}
|
||||
onReset();
|
||||
tableRef.value.requestServerInteraction(); // 刷新列表
|
||||
editFormShow.value = false;
|
||||
successNotify('保存成功!');
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
} finally {
|
||||
operateDisabled.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteRoleData(row: RoleInfo) {
|
||||
operateDisabled.value = true;
|
||||
$q.dialog({
|
||||
title: '确认',
|
||||
message: `确认删除角色 "${row.name}" 吗?`,
|
||||
cancel: true,
|
||||
})
|
||||
.onOk(async () => {
|
||||
try {
|
||||
await deleteRole(row.id);
|
||||
tableRef.value.requestServerInteraction(); // 刷新列表
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
}
|
||||
})
|
||||
.onDismiss(() => {
|
||||
operateDisabled.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
roleInfo.id = '';
|
||||
roleInfo.name = '';
|
||||
roleInfo.editPaths = [];
|
||||
roleInfo.paths = [];
|
||||
myForm.value?.resetValidation();
|
||||
}
|
||||
|
||||
function pathSelectsed(val: PathItem[]) {
|
||||
roleInfo.editPaths = val.map((item) => item.id);
|
||||
}
|
||||
</script>
|
@ -24,16 +24,83 @@
|
||||
></q-input>
|
||||
<q-btn flat round color="primary" icon="search" />
|
||||
</template>
|
||||
<template v-slot:body-cell-roles="props">
|
||||
<q-td :props="props">
|
||||
<div class="q-gutter-sm row justify-center">
|
||||
<q-chip
|
||||
outline
|
||||
size="sm"
|
||||
color="primary"
|
||||
v-for="(item, index) in props.row.roleList"
|
||||
:key="index"
|
||||
>
|
||||
{{ item.roleName }}
|
||||
</q-chip>
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
<template v-slot:body-cell-operations="props">
|
||||
<q-td :props="props">
|
||||
<div class="q-gutter-sm row justify-center">
|
||||
<q-btn
|
||||
color="primary"
|
||||
label="编辑角色"
|
||||
:disable="operateDisabled"
|
||||
@click="edieUserData(props.row)"
|
||||
/>
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
|
||||
<q-dialog
|
||||
v-model="editFormShow"
|
||||
persistent
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<q-card style="width: 300px">
|
||||
<q-card-section>
|
||||
<q-form
|
||||
ref="myForm"
|
||||
@submit="edieUserRole"
|
||||
@reset="onReset"
|
||||
class="q-gutter-md"
|
||||
>
|
||||
<div class="text-h6">修改用户角色</div>
|
||||
<q-input outlined disable label="用户名" v-model="userInfo.name" />
|
||||
<q-select
|
||||
outlined
|
||||
v-model="userInfo.Rids"
|
||||
:options="options"
|
||||
multiple
|
||||
map-options
|
||||
emit-value
|
||||
label="用户角色"
|
||||
lazy-rules
|
||||
:rules="[(val) => val.length > 0 || '请选择角色!']"
|
||||
></q-select>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn color="primary" label="保存" type="submit" />
|
||||
<q-btn label="取消" type="reset" v-close-popup />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { type QTableColumn } from 'quasar';
|
||||
import { pageQuery } from '../api/UserApi';
|
||||
import { errorNotify } from '../utils/CommonNotify';
|
||||
import { QForm, useQuasar, type QTableColumn } from 'quasar';
|
||||
import { pageQuery, User } from '../api/UserApi';
|
||||
import { errorNotify, successNotify } from '../utils/CommonNotify';
|
||||
import { ApiError } from 'src/boot/axios';
|
||||
import { pageQueryRole, userLinkRole } from 'src/api/AuthApi';
|
||||
|
||||
const $q = useQuasar();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
sizeHeight: number;
|
||||
@ -46,6 +113,7 @@ const tableHeight = computed(() => {
|
||||
|
||||
onMounted(() => {
|
||||
tableRef.value.requestServerInteraction();
|
||||
getAllRole();
|
||||
});
|
||||
|
||||
const columnDefs: QTableColumn[] = [
|
||||
@ -57,6 +125,13 @@ const columnDefs: QTableColumn[] = [
|
||||
align: 'center',
|
||||
},
|
||||
{ name: 'id', label: '用户ID', field: 'id', align: 'center' },
|
||||
{
|
||||
name: 'roles',
|
||||
label: '角色',
|
||||
field: 'roles',
|
||||
required: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'registerTime',
|
||||
label: '创建时间',
|
||||
@ -69,6 +144,7 @@ const columnDefs: QTableColumn[] = [
|
||||
field: 'mobile',
|
||||
align: 'center',
|
||||
},
|
||||
{ name: 'operations', label: '操作', field: 'operations', align: 'center' },
|
||||
];
|
||||
|
||||
const tableRef = ref();
|
||||
@ -85,6 +161,7 @@ const pagination = ref({
|
||||
rowsNumber: 10,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
async function onRequest(props: any) {
|
||||
const { page, rowsPerPage, sortBy, descending } = props.pagination;
|
||||
const filter = props.filter;
|
||||
@ -109,4 +186,90 @@ async function onRequest(props: any) {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const editFormShow = ref(false);
|
||||
|
||||
interface UserInfo extends Omit<User, 'password'> {
|
||||
Rids: number[];
|
||||
}
|
||||
const userInfo = reactive<UserInfo>({
|
||||
id: '',
|
||||
name: '',
|
||||
mobile: '',
|
||||
registerTime: '',
|
||||
roleList: [],
|
||||
Rids: [],
|
||||
});
|
||||
|
||||
const options: { label: string; value: number }[] = [];
|
||||
function getAllRole() {
|
||||
pageQueryRole({
|
||||
current: 1,
|
||||
size: 999,
|
||||
})
|
||||
.then((res) => {
|
||||
res.records.forEach((item) => {
|
||||
const obj = {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
};
|
||||
options.push(obj);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
//编辑用户
|
||||
function edieUserData(row: User) {
|
||||
userInfo.id = row.id;
|
||||
userInfo.name = row.name;
|
||||
userInfo.mobile = row.mobile;
|
||||
userInfo.registerTime = row.registerTime;
|
||||
userInfo.roleList = row.roleList;
|
||||
userInfo.Rids = row.roleList.map((item) => {
|
||||
return item.roleId;
|
||||
});
|
||||
editFormShow.value = true;
|
||||
}
|
||||
|
||||
const myForm = ref<QForm | null>(null);
|
||||
|
||||
//提交编辑用户
|
||||
const operateDisabled = ref(false);
|
||||
function edieUserRole() {
|
||||
myForm.value?.validate().then(async (res) => {
|
||||
if (res) {
|
||||
operateDisabled.value = true;
|
||||
try {
|
||||
await userLinkRole({
|
||||
id: +userInfo.id,
|
||||
roleList: userInfo.Rids,
|
||||
});
|
||||
tableRef.value.requestServerInteraction(); // 刷新列表
|
||||
editFormShow.value = false;
|
||||
successNotify('修改成功');
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
} finally {
|
||||
operateDisabled.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
userInfo.id = '';
|
||||
userInfo.name = '';
|
||||
userInfo.mobile = '';
|
||||
userInfo.registerTime = '';
|
||||
userInfo.roleList = [];
|
||||
userInfo.Rids = [];
|
||||
myForm.value?.resetValidation();
|
||||
}
|
||||
</script>
|
||||
|
@ -70,6 +70,22 @@ const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
component: () => import('pages/UserManage.vue'),
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
name: 'role',
|
||||
meta: {
|
||||
description: '权限管理',
|
||||
},
|
||||
component: () => import('pages/RoleManage.vue'),
|
||||
},
|
||||
{
|
||||
path: 'authPath',
|
||||
name: 'authPath',
|
||||
meta: {
|
||||
description: '权限接口管理',
|
||||
},
|
||||
component: () => import('pages/AuthPathManage.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -2438,9 +2438,9 @@ isobject@^3.0.1:
|
||||
resolved "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||
integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
|
||||
|
||||
"jl-graphic@git+https://gitea.joylink.club/joylink/graphic-pixi.git#v0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "git+https://gitea.joylink.club/joylink/graphic-pixi.git#100ddafc75ffa2fc646ad26359682e0f083511e3"
|
||||
"jl-graphic@git+https://gitea.joylink.club/joylink/graphic-pixi.git#v0.1.15":
|
||||
version "0.1.14"
|
||||
resolved "git+https://gitea.joylink.club/joylink/graphic-pixi.git#8b0ad14f7324a5eaba58239645a1fa0452e87ab4"
|
||||
dependencies:
|
||||
"@pixi/graphics-extras" "^7.3.2"
|
||||
"@pixi/utils" "^7.3.2"
|
||||
|
Loading…
Reference in New Issue
Block a user