增加GraphiQL形式的数据请求+iscs草稿图的创建删除等
This commit is contained in:
parent
8e01da8ed5
commit
f9ee903d60
|
@ -0,0 +1,42 @@
|
|||
export class PageQueryDto {
|
||||
current: number;
|
||||
size: number;
|
||||
orders?: OrderItemDto[];
|
||||
constructor(current: number, size: number, orders?: OrderItemDto[]) {
|
||||
this.current = current;
|
||||
this.size = size;
|
||||
this.orders = orders;
|
||||
}
|
||||
}
|
||||
|
||||
export class OrderItemDto {
|
||||
column: string;
|
||||
asc: boolean;
|
||||
constructor(column: string, asc: boolean) {
|
||||
this.column = column;
|
||||
this.asc = asc;
|
||||
}
|
||||
|
||||
static asc(column: string): OrderItemDto {
|
||||
return new OrderItemDto(column, true);
|
||||
}
|
||||
static desc(column: string): OrderItemDto {
|
||||
return new OrderItemDto(column, false);
|
||||
}
|
||||
}
|
||||
|
||||
export interface PageDto<T = unknown> {
|
||||
data: T[];
|
||||
/**
|
||||
* 记录总数
|
||||
*/
|
||||
total: number;
|
||||
/**
|
||||
* 第几页
|
||||
*/
|
||||
current: number;
|
||||
/**
|
||||
* 每页数量
|
||||
*/
|
||||
size: number;
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
import { api } from 'src/boot/axios';
|
||||
import { PageDto } from './ApiCommon';
|
||||
|
||||
const DraftUriBase = '';
|
||||
|
||||
export enum DraftDataType {
|
||||
UNKNOWN,
|
||||
EM = 'EM',
|
||||
IBP = 'IBP',
|
||||
PSL = 'PSL',
|
||||
ISCS = 'ISCS',
|
||||
}
|
||||
|
||||
export interface DraftItem {
|
||||
id: number;
|
||||
name: string;
|
||||
dataType: DraftDataType;
|
||||
data: [];
|
||||
userId: number;
|
||||
isShared: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface PagingQueryParams {
|
||||
paging: {
|
||||
page: number;
|
||||
itemsPerPage: number;
|
||||
};
|
||||
query: {
|
||||
userId: number;
|
||||
dataType: DraftDataType;
|
||||
name?: string;
|
||||
isShared?: boolean;
|
||||
};
|
||||
}
|
||||
export async function draftPageQuery(
|
||||
params: PagingQueryParams
|
||||
): Promise<PageDto<DraftItem>> {
|
||||
const query = `
|
||||
query userDraftDataPaging($paging: PageQueryDto, $query: UserDraftDataFilterDto) {
|
||||
userDraftDataPaging(paging: $paging, query: $query) {
|
||||
total
|
||||
data {
|
||||
id name dataType createdAt updatedAt isShared
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
const response = await api.post(``, {
|
||||
query,
|
||||
variables: params,
|
||||
});
|
||||
return response.data.data.userDraftDataPaging;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建草稿
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function createDraft(name: string) {
|
||||
const mutation = `
|
||||
mutation createDraftData($input: CreateDraftDataDto) {
|
||||
createDraftData(input: $input) {
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
const variables = {
|
||||
input: {
|
||||
name,
|
||||
dataType: DraftDataType.ISCS,
|
||||
data: [],
|
||||
userId: 1,
|
||||
},
|
||||
};
|
||||
return api.post(``, {
|
||||
query: mutation,
|
||||
variables,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除草稿
|
||||
* @param id 草稿id
|
||||
*/
|
||||
export function deleteDraft(id: number) {
|
||||
const mutation = `
|
||||
mutation deleteDraftData($id: Int) {
|
||||
deleteDraftData(id: $id)
|
||||
}
|
||||
`;
|
||||
const variables = {
|
||||
id,
|
||||
};
|
||||
return api.post(``, {
|
||||
query: mutation,
|
||||
variables,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取草稿数据
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export async function getDraft(id: number): Promise<DraftItem> {
|
||||
const response = await api.get(`${DraftUriBase}/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存草稿数据
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export function saveDraft(
|
||||
id: number,
|
||||
data: {
|
||||
proto: string;
|
||||
}
|
||||
) {
|
||||
return api.put(`${DraftUriBase}/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 另存草稿数据
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export async function saveAsDraft(
|
||||
id: number,
|
||||
data: { name: string; proto: string }
|
||||
): Promise<DraftItem> {
|
||||
const response = await api.post(`${DraftUriBase}/${id}/saveAs`, data);
|
||||
return response.data;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import * as GraphicsExtras from '@pixi/graphics-extras';
|
||||
// "async" is optional;
|
||||
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
|
||||
export default boot(async (/* { app, router, ... } */) => {
|
||||
GraphicsExtras;
|
||||
});
|
|
@ -0,0 +1,124 @@
|
|||
import axios, { AxiosInstance } from 'axios';
|
||||
import { AxiosError } from 'axios';
|
||||
import { Dialog } from 'quasar';
|
||||
import { boot } from 'quasar/wrappers';
|
||||
//import { getJwtToken } from 'src/configs/TokenManage';
|
||||
import { getHttpBase } from 'src/configs/UrlManage';
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$axios: AxiosInstance;
|
||||
}
|
||||
}
|
||||
|
||||
interface ErrorData {
|
||||
status: number;
|
||||
title: string;
|
||||
detail: string;
|
||||
code: number;
|
||||
}
|
||||
|
||||
export class ApiError {
|
||||
origin: AxiosError;
|
||||
/**
|
||||
* 业务错误代码
|
||||
*/
|
||||
code: number;
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* 相关问题描述
|
||||
*/
|
||||
detail?: string;
|
||||
constructor(origin: AxiosError<unknown, unknown>) {
|
||||
this.origin = origin;
|
||||
const response = origin.response;
|
||||
if (response) {
|
||||
const err = response.data as ErrorData;
|
||||
this.code = err.code;
|
||||
this.title = err.title;
|
||||
this.detail = err.detail;
|
||||
} else {
|
||||
this.code = origin.status || -1;
|
||||
this.title = origin.message;
|
||||
}
|
||||
}
|
||||
|
||||
static from(err: AxiosError<unknown, unknown>): ApiError {
|
||||
return new ApiError(err);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否认证失败(登录过期)
|
||||
* @returns
|
||||
*/
|
||||
isAuthError(): boolean {
|
||||
return this.origin.response?.status === 401;
|
||||
}
|
||||
}
|
||||
|
||||
// Be careful when using SSR for cross-request state pollution
|
||||
// due to creating a Singleton instance here;
|
||||
// If any client changes this (global) instance, it might be a
|
||||
// good idea to move this instance creation inside of the
|
||||
// "export default () => {}" function below (which runs individually
|
||||
// for each client)
|
||||
const api = axios.create({ baseURL: getHttpBase() });
|
||||
let isOpenDialog = false; // 认证弹窗是否打开
|
||||
|
||||
//const CancelToken = axios.CancelToken;
|
||||
//const source = CancelToken.source();
|
||||
export default boot(({ app, router }) => {
|
||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||
|
||||
// 拦截请求,添加
|
||||
/* api.interceptors.request.use(
|
||||
(config) => {
|
||||
config.headers.Authorization = getJwtToken();
|
||||
config.cancelToken = source.token;
|
||||
if (isOpenDialog) {
|
||||
source.cancel();
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(err: AxiosError) => {
|
||||
return Promise.reject(ApiError.from(err));
|
||||
}
|
||||
); */
|
||||
|
||||
api.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
(err) => {
|
||||
if (err.response && err.response.status === 401 && !isOpenDialog) {
|
||||
isOpenDialog = true;
|
||||
Dialog.create({
|
||||
title: '认证失败',
|
||||
message: '认证失败或登录超时,请重新登录',
|
||||
persistent: true,
|
||||
})
|
||||
.onOk(() => {
|
||||
router.push({ name: 'login' });
|
||||
isOpenDialog = false;
|
||||
})
|
||||
.onCancel(() => {
|
||||
isOpenDialog = false;
|
||||
});
|
||||
}
|
||||
return Promise.reject(ApiError.from(err));
|
||||
}
|
||||
);
|
||||
|
||||
app.config.globalProperties.$axios = axios;
|
||||
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||
// so you won't necessarily have to import axios in each vue file
|
||||
|
||||
app.config.globalProperties.$api = api;
|
||||
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||
// so you can easily perform requests against your app's API
|
||||
});
|
||||
|
||||
export { api };
|
|
@ -0,0 +1,55 @@
|
|||
function getHost(): string {
|
||||
if (process.env.URL_ENV == 'test') {
|
||||
return 'test.joylink.club/bjrtsts-server';
|
||||
} else if (process.env.URL_ENV == 'publish') {
|
||||
return 'joylink.club/bjrtsts-server';
|
||||
} else if (process.env.URL_ENV == 'local_test') {
|
||||
return '192.168.33.233:8765';
|
||||
} else if (process.env.URL_ENV == 'local_pxf') {
|
||||
//北京现场
|
||||
|
||||
return '172.29.5.168/bjrtss-server';
|
||||
}
|
||||
|
||||
// return '192.168.3.7:9091';
|
||||
// return '192.168.3.47:9091';
|
||||
// return '192.168.3.37:9091';
|
||||
//return '192.168.33.207:9091'; // 张骞
|
||||
// return '192.168.33.93:9091';
|
||||
// return '192.168.3.37:9091'; //卫志宏
|
||||
// return 'test.joylink.club/bjrtsts-service'; // 测试
|
||||
return '192.168.33.233:8765';
|
||||
}
|
||||
|
||||
export function getHttpBase() {
|
||||
let protocol = 'http';
|
||||
if (['publish'].includes(process.env.URL_ENV as string)) {
|
||||
protocol = 'https';
|
||||
}
|
||||
return `${protocol}://${getHost()}`;
|
||||
}
|
||||
|
||||
export function getWebsocketUrl() {
|
||||
let protocol = 'ws';
|
||||
let host = '192.168.33.233';
|
||||
// let host = 'test.joylink.club';
|
||||
let port = '8083';
|
||||
let url = `${protocol}://${host}:${port}`;
|
||||
if (process.env.URL_ENV == 'test') {
|
||||
// protocol = 'wss';
|
||||
host = 'test.joylink.club/bjrtsts-server';
|
||||
port = '';
|
||||
url = `${protocol}://${host}`;
|
||||
} else if (process.env.URL_ENV == 'publish') {
|
||||
protocol = 'wss';
|
||||
host = 'joylink.club/bjrtsts-server';
|
||||
port = '';
|
||||
url = `${protocol}://${host}`;
|
||||
} else if (process.env.URL_ENV == 'local_test') {
|
||||
host = '192.168.33.233';
|
||||
} else if (process.env.URL_ENV == 'local_pxf') {
|
||||
host = '172.29.5.168';
|
||||
}
|
||||
|
||||
return `${url}/mqtt`;
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
@click="toggleLeftDrawer"
|
||||
/>
|
||||
|
||||
<q-toolbar-title> 北京玖琏 </q-toolbar-title>
|
||||
<q-toolbar-title> 城市轨道交通平台 </q-toolbar-title>
|
||||
|
||||
<div class="q-gutter-sm no-wrap">
|
||||
<q-btn
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-table
|
||||
ref="tableRef"
|
||||
title="草稿图"
|
||||
:style="{ height: tableHeight + 'px' }"
|
||||
: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="createFormShow = 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"
|
||||
:disable="operateDisabled"
|
||||
label="编辑"
|
||||
@click="goToPath(props.row)"
|
||||
/>
|
||||
<q-btn
|
||||
color="primary"
|
||||
:disable="operateDisabled"
|
||||
label="发布"
|
||||
@click="prePublish(props.row)"
|
||||
/>
|
||||
<q-btn
|
||||
color="red"
|
||||
:disable="operateDisabled"
|
||||
label="删除"
|
||||
@click="deleteData(props.row)"
|
||||
/>
|
||||
</div>
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
|
||||
<q-dialog
|
||||
v-model="createFormShow"
|
||||
persistent
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<q-card style="width: 300px">
|
||||
<q-card-section>
|
||||
<q-form ref="myForm" @submit="onCreate" class="q-gutter-md">
|
||||
<div class="text-h6">新建草稿图</div>
|
||||
<q-input
|
||||
outlined
|
||||
label="名称 * "
|
||||
v-model="draftName"
|
||||
lazy-rules
|
||||
:rules="[(val) => val.length > 0 || '请输入名称!']"
|
||||
/>
|
||||
<q-card-actions align="right">
|
||||
<q-btn color="primary" label="创建" type="submit" />
|
||||
<q-btn label="取消" v-close-popup />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog
|
||||
v-model="publishFormShow"
|
||||
persistent
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
>
|
||||
<q-card style="width: 300px">
|
||||
<q-card-section>
|
||||
<q-form ref="pubForm" @submit="publishGraphics" class="q-gutter-md">
|
||||
<div class="text-h6">草稿发布</div>
|
||||
<q-input
|
||||
outlined
|
||||
disable
|
||||
label="草稿名称"
|
||||
v-model="publishForm.draftName"
|
||||
/>
|
||||
<q-input
|
||||
outlined
|
||||
label="发布名称 * "
|
||||
v-model="publishForm.pubName"
|
||||
lazy-rules
|
||||
:rules="[(val) => val.length > 0 || '请输入名称!']"
|
||||
/>
|
||||
<q-input
|
||||
outlined
|
||||
label="备注 * "
|
||||
v-model="publishForm.note"
|
||||
lazy-rules
|
||||
:rules="[(val) => val.length > 0 || '请输入备注!']"
|
||||
/>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn color="primary" label="发布" type="submit" />
|
||||
<q-btn label="取消" 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 { useQuasar, type QTableColumn, QForm } from 'quasar';
|
||||
import {
|
||||
createDraft,
|
||||
deleteDraft,
|
||||
DraftDataType,
|
||||
DraftItem,
|
||||
draftPageQuery,
|
||||
} from '../api/DraftApi';
|
||||
import { ApiError } from 'src/boot/axios';
|
||||
|
||||
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: 'name',
|
||||
label: '名称',
|
||||
field: 'name',
|
||||
required: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: '创建时间',
|
||||
field: (row) => new Date(row.createdAt).toLocaleString(),
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: '更新时间',
|
||||
field: (row) => new Date(row.updatedAt).toLocaleString(),
|
||||
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 } = props.pagination;
|
||||
const filter = props.filter;
|
||||
|
||||
loading.value = true;
|
||||
const variables = {
|
||||
paging: {
|
||||
page: page,
|
||||
itemsPerPage: rowsPerPage,
|
||||
},
|
||||
query: { userId: 1, dataType: DraftDataType.ISCS, name: filter.name },
|
||||
};
|
||||
try {
|
||||
let response = await draftPageQuery(variables);
|
||||
const pageData = response;
|
||||
pagination.value.rowsNumber = pageData.total;
|
||||
rows.splice(0, rows.length, ...(pageData.data as []));
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const createFormShow = ref(false);
|
||||
const draftName = ref('');
|
||||
const myForm = ref<QForm | null>(null);
|
||||
function onCreate() {
|
||||
myForm.value?.validate().then(async (res) => {
|
||||
if (res) {
|
||||
operateDisabled.value = true;
|
||||
try {
|
||||
await createDraft(draftName.value);
|
||||
createFormShow.value = false;
|
||||
tableRef.value.requestServerInteraction(); // 刷新列表
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
} finally {
|
||||
operateDisabled.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const pubForm = ref<QForm | null>(null);
|
||||
const publishFormShow = ref(false);
|
||||
const publishForm = reactive({
|
||||
id: '',
|
||||
draftName: '',
|
||||
pubName: '',
|
||||
note: '',
|
||||
});
|
||||
function prePublish(row: DraftItem) {
|
||||
publishFormShow.value = true;
|
||||
publishForm.id = row.id + '';
|
||||
publishForm.draftName = row.name;
|
||||
publishForm.pubName = row.name;
|
||||
publishForm.note = '';
|
||||
}
|
||||
|
||||
async function deleteData(row: DraftItem) {
|
||||
operateDisabled.value = true;
|
||||
$q.dialog({
|
||||
title: '确认',
|
||||
message: `确认删除草稿图 "${row.name}" 吗?`,
|
||||
cancel: true,
|
||||
})
|
||||
.onOk(async () => {
|
||||
try {
|
||||
await deleteDraft(row.id);
|
||||
tableRef.value.requestServerInteraction(); // 刷新列表
|
||||
} catch (err) {
|
||||
const error = err as ApiError;
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.title,
|
||||
});
|
||||
}
|
||||
})
|
||||
.onDismiss(() => {
|
||||
operateDisabled.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
|
@ -17,8 +17,35 @@ const routes: RouteRecordRaw[] = [
|
|||
// children: [{ path: '', component: () => import('pages/IndexPage.vue') }],
|
||||
},
|
||||
|
||||
// Always leave this as last one,
|
||||
// but you can also remove it
|
||||
{
|
||||
path: '/dataManage',
|
||||
name: 'dataManage',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
meta: {
|
||||
label: '数据管理',
|
||||
icon: 'list_alt',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'iscsDraft',
|
||||
name: 'iscsDraft',
|
||||
component: () => import('pages/IscsDraftManage.vue'),
|
||||
meta: {
|
||||
label: 'iscs草稿管理',
|
||||
icon: 'app_registration',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'iscsPublish',
|
||||
name: 'iscsPublish',
|
||||
component: () => import('pages/PublishManage.vue'),
|
||||
meta: {
|
||||
label: 'iscs发布数据管理',
|
||||
icon: 'playlist_add_check',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/:catchAll(.*)*',
|
||||
meta: {
|
||||
|
|
Loading…
Reference in New Issue