Commit 42c2ae7c authored by 陈浩玮's avatar 陈浩玮

Merge branch 'master' into 'feature/chw'

# Conflicts:
#   src/utils/index.js
parents de87c07c 46a55538
This diff is collapsed.
......@@ -3,6 +3,7 @@ import Lang from '@/utils/langUtils';
export * from './menu';
export * from './system';
export * from './task';
export * from './xunjian';
export function getUserDetailInfoApi() {
return request('/api/v1/detail', METHOD.GET);
......
......@@ -16,6 +16,10 @@ export async function getMenuDataApi(useCache) {
});
}
export function getMenuDetailApi(id) {
return getReq(`/api/v1/menus/${id}`);
}
export function addMenuApi(data) {
return postReq('/api/v1/menus', data);
}
......
import { getReq, postReq } from '@/utils/requestUtil';
import langUtils from '@/utils/langUtils';
export function getXunJianDownloadUrlApi(idList) {
return getReq('/ranger/inspection/api/v1/jobs/export', { jobIdList: idList.join(',') });
}
export function getAreaListDataApi() {
return getReq(`/ranger/inspection/api/v1/region/list`);
}
export function getBusinessListApi() {
return getReq(
`/api/v1/parameters/business/list?paramModule=rpis_route_schedule&paramCode=inspection_state&paramLocale=${langUtils.get()}`,
);
}
export function getBanZuListApi() {
return getReq(
`/api/v1/parameters/business/list?paramModule=rpis_route_schedule&paramCode=shift_type&paramLocale=${langUtils.get()}`,
);
}
export function getTaskTypeApi() {
return getReq(
`/api/v1/parameters/business/list?paramModule=rpis_route_schedule&paramCode=job_type&paramLocale=${langUtils.get()}`,
);
}
export function addXunJianTaskApi(data) {
return postReq('/ranger/inspection/api/v1/jobs/temporary', data);
}
......@@ -24,6 +24,8 @@ export default {
request: Function,
formatData: { type: Object, default: undefined },
getName: Function,
labelFiled: String, // 和 formatData 一样 传一种就行了
valueFiled: String,
},
data() {
return {
......@@ -34,6 +36,9 @@ export default {
if (this.request) {
const newArr = await this.request();
this.data = this.formatData ? await formatObj(newArr, this.formatData) : newArr;
this.data = this.labelFiled
? formatObj(newArr, { label: this.labelFiled, value: this.valueFiled, key: this.valueFiled })
: this.data;
}
},
methods: {
......
<template>
<a-select
show-search
placeholder="input search text"
:default-active-first-option="false"
:show-arrow="false"
:filter-option="false"
@search="handleSearch"
allowClear
:options="options"
:mode="mode"
v-on="$listeners"
/>
</template>
<script>
import { getReq } from '@/utils/requestUtil';
import { formatObj } from '@/utils';
export default {
props: {
url: String,
searchField: String,
mode: String,
labelFiled: String,
valueFiled: String,
},
model: {
prop: 'value',
event: 'change',
},
data() {
return {
options: [],
};
},
mounted() {
this.getData('');
},
methods: {
async getData(searchString) {
this.options = formatObj(await getReq(this.url, { [this.searchField]: searchString }), {
label: this.labelFiled,
value: this.valueFiled,
key: this.valueFiled,
});
},
handleSearch(value) {
if (this.timeOut) clearTimeout(this.timeOut);
this.timeOut = setTimeout(() => {
if (value) this.getData(value);
}, 300);
},
},
};
</script>
<template>
<a-select
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="Please select"
allow-clear
:options="options"
v-on="$listeners"
v-bind="$attrs"
/>
</template>
<script>
import { getReq } from '@/utils/requestUtil';
import { formatObj } from '@/utils';
export default {
props: {
url: String,
labelFiled: String,
valueFiled: String,
},
data: () => ({
options: [],
}),
model: {
prop: 'value',
event: 'change',
},
watch: {
url: 'getData',
},
methods: {
async getData() {
if (this.url) {
this.options = formatObj(await getReq(this.url), {
label: this.labelFiled,
value: this.valueFiled,
key: this.valueFiled,
});
}
},
},
mounted() {
this.getData();
},
};
</script>
......@@ -69,7 +69,7 @@
</a-space>
</template>
</a-drawer>
<slot name="children"></slot>
<slot name="children" />
</div>
</template>
......
<template>
<div>
<my-card v-if="$scopedSlots.search">
<my-card v-if="$scopedSlots.search" class="tw-mb-2.5">
<a-form-model :model="queryForm" layout="horizontal">
<a-row :gutter="16">
<slot name="search" :query="queryForm" />
......@@ -16,9 +16,9 @@
</my-card>
<my-card>
<a-space :class="{ 'tw-mb-2.5': !!$scopedSlots.search }">
<a-button type="primary" v-if="addBtn" @click="addBtnClick">
{{ typeof addBtn === 'object' ? addBtn.text : '新增' }}
<a-space class="tw-mb-2">
<a-button type="primary" v-if="newBtn" @click="addBtnClick">
{{ newBtn.text || '新增' }}
</a-button>
<slot name="operation" />
</a-space>
......@@ -27,8 +27,10 @@
:data-source="data"
:loading="loading"
v-bind="$attrs"
:rowKey="rowKey"
:pagination="pagination"
@change="pageChange"
:row-selection="selected ? rowSelection : undefined"
>
<slot />
<a-table-column title="操作" v-if="buttons">
......@@ -48,8 +50,9 @@
:width="600"
@close="hidden"
:maskClosable="false"
:title="title"
>
<slot name="drawer" :hidden="hidden" />
<slot name="drawer" :hidden="hidden" :refresh="getData" />
</a-drawer>
</div>
</template>
......@@ -62,6 +65,8 @@ const initQuery = {
pageNum: 1,
};
const defaultTitle = '新增';
export default {
props: {
url: String,
......@@ -69,9 +74,12 @@ export default {
buttons: Array,
noPage: Boolean,
formatData: Function,
rowKey: [String, Function],
selected: Array,
},
data() {
const newBtn = this.addBtn ? (typeof this.addBtn === 'object' ? this.addBtn : {}) : this.addBtn;
return {
initQuery: {
...initQuery,
......@@ -81,7 +89,7 @@ export default {
loading: false,
total: 0,
visible: false,
title: '新增',
title: newBtn.title ?? defaultTitle,
drawerStyle: {
display: 'flex',
flexDirection: 'column',
......@@ -99,6 +107,9 @@ export default {
},
computed: {
newBtn() {
return this.addBtn ? (typeof this.addBtn === 'object' ? this.addBtn : {}) : this.addBtn;
},
pagination() {
return this.noPage
? false
......@@ -109,6 +120,13 @@ export default {
showQuickJumper: true,
};
},
rowSelection() {
return {
onChange: (selectedRowKeys, selectedRows) => {
this.$emit('update:selected', [selectedRowKeys, selectedRows]);
},
};
},
},
methods: {
......@@ -142,21 +160,29 @@ export default {
hidden() {
this.visible = false;
this.title = this.addBtn?.title ?? defaultTitle;
},
show({ title } = {}) {
this.visible = true;
if (title) this.title = title;
},
reset() {
this.queryForm = {};
this.initQuery = { ...initQuery };
this.getData();
},
addBtnClick() {
const { click } = typeof this.addBtn === 'object' ? this.addBtn : {};
click && click();
this.visible = true;
},
getFormRef(ref) {
console.log(ref);
},
},
};
</script>
......@@ -31,6 +31,10 @@ export default {
type: Function,
default: EMPTY_FUN,
},
refresh: {
type: Function,
default: EMPTY_FUN,
},
},
data: () => ({
loading: false,
......@@ -43,9 +47,14 @@ export default {
async ok() {
this.loading = true;
await this.onOk();
try {
await this.onOk();
this.hidden();
this.refresh();
} catch (error) {
// todo
}
this.loading = false;
this.hidden();
},
},
};
......
......@@ -15,7 +15,9 @@
:class="['icon-sync', { hide: page.fullPath !== active && !page.loading }]"
:type="page.loading ? 'loading' : 'sync'"
/>
<div class="title" @click="onTabClick(page.fullPath)">{{ pageName(page) }}</div>
<div class="tw-inline-block tw-select-none tw-h-full" @click="onTabClick(page.fullPath)">
{{ pageName(page) }}
</div>
<a-icon
v-if="!page.unclose"
@click="onClose(page.fullPath)"
......@@ -102,12 +104,7 @@ export default {
font-size: 14px;
user-select: none;
transition: all 0.2s;
.title {
display: inline-block;
height: 100%;
user-select: none;
-webkit-user-select: none;
}
.icon-close {
font-size: 12px;
margin-left: 6px;
......
......@@ -3,7 +3,7 @@ import { formatFullPath } from '@/utils/i18nUtil';
import { filterMenu } from '@/utils/authorityUtil';
import { getLocalSetting } from '@/utils/themeUtil';
import { langUtil } from '@/utils';
import deepClone from 'lodash.clonedeep';
import deepClone from 'lodash/cloneDeep';
const localSetting = getLocalSetting(true);
const customTitlesStr = sessionStorage.getItem(process.env.VUE_APP_TBAS_TITLES_KEY);
......
<template>
<exception-page home-route="/dashboard/workplace" :style="`min-height: ${minHeight}`" type="403" />
<exception-page home-route="/" :style="`min-height: ${minHeight}`" type="403" />
</template>
<script>
......@@ -16,6 +16,3 @@ export default {
},
};
</script>
<style scoped lang="less">
</style>
<template>
<exception-page home-route="/dashboard/workplace" :style="`min-height: ${minHeight}`" type="500" />
<exception-page home-route="/" :style="`min-height: ${minHeight}`" type="500" />
</template>
<script>
......@@ -16,6 +16,3 @@ export default {
},
};
</script>
<style scoped lang="less">
</style>
......@@ -10,10 +10,10 @@
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="中文名称">
<a-input v-model="form.menuName" />
<a-input v-model="menuTypeNameZh" />
</a-form-model-item>
<a-form-model-item label="英文名称">
<a-input />
<a-input v-model="menuTypeNameEn" />
</a-form-model-item>
<a-form-model-item label="模块URL" prop="menuUrl">
<a-input v-model="form.menuUrl" />
......@@ -31,7 +31,7 @@
</template>
<script>
import { addMenuApi, updateMenuApi } from '@/api';
import { addMenuApi, updateMenuApi, getMenuDetailApi } from '@/api';
import MenuTree from '@/components/menu_tree/select.vue';
export default {
......@@ -42,7 +42,15 @@ export default {
menuType: 'CATALOG',
parentMenuId: null,
menuName: '',
menuNameI18nList: [
{ i18nLocale: 'zh_CN', i18nMessage: '' },
{ i18nLocale: 'en_US', i18nMessage: '' },
],
menuRemark: '',
},
menuTypeNameZh: '',
menuTypeNameEn: '',
rules: {
menuUrl: [{ required: true }],
},
......@@ -58,12 +66,29 @@ export default {
methods: {
async submit() {
await this.$refs['form'].validate();
const reqData = { ...this.form, parentMenuId: this.form.parentMenuId ?? 0 };
console.log('this.form.enuNameI18nList', this.form.enuNameI18nList);
const reqData = {
...this.form,
parentMenuId: this.form.parentMenuId ?? 0,
menuNameI18nList: this.form.menuNameI18nList.map(i18 => {
if (i18.i18nLocale === 'en_US') i18.i18nMessage = this.menuTypeNameEn;
if (i18.i18nLocale === 'zh_CN') i18.i18nMessage = this.menuTypeNameZh;
return i18;
}),
menuName: this.menuTypeNameZh,
};
console.log('this.form.enuNameI18nList', this.form.enuNameI18nList);
return this.isEdit ? updateMenuApi(reqData) : addMenuApi(reqData);
},
setEdit(data) {
async setEdit(data) {
this.type = 1;
this.form = data;
this.form = await getMenuDetailApi(data.menuId);
this.form.menuNameI18nList?.forEach(i18 => {
if (i18.i18nLocale === 'en_US') this.menuTypeNameEn = i18.i18nMessage;
if (i18.i18nLocale === 'zh_CN') this.menuTypeNameZh = i18.i18nMessage;
});
},
},
};
......
<template>
<Wraper :hidden="hidden" :onOk="submit" :refresh="refresh">
<a-form-model layout="vertical" :model="form" :rules="rules" ref="form">
<a-form-model-item label="地区" prop="regionId">
<RequestSelect
:request="getAreaListDataApi"
v-model="form.regionId"
labelFiled="regionName"
valueFiled="regionId"
/>
</a-form-model-item>
<a-form-model-item label="线路" prop="routeId">
<UrlSelect
:url="xianLuUrl"
labelFiled="routeName"
valueFiled="routeId"
v-model="form.routeId"
/>
</a-form-model-item>
<a-form-model-item label="班组" prop="shiftType">
<RequestSelect
:request="getBanZuListApi"
v-model="form.shiftType"
labelFiled="paramName"
valueFiled="paramValue"
/>
</a-form-model-item>
<a-form-model-item label="任务时间" prop="jobTime">
<a-date-picker
showTime
class="tw-w-full"
valueFormat="YYYY-MM-DD HH:mm:ss"
v-model="form.jobTime"
/>
</a-form-model-item>
<a-form-model-item label="巡检人" prop="inspectionIdList">
<SearchSelect
url="/api/v1/users/searching"
searchField="userName"
labelFiled="userName"
valueFiled="userId"
v-model="form.inspectionIdList"
mode="multiple"
/>
</a-form-model-item>
</a-form-model>
</Wraper>
</template>
<script>
import Wraper from '@/components/table/wraper.vue';
import RequestSelect from '@/components/MySelect/RequestSelect.vue';
import SearchSelect from '@/components/MySelect/search_select.vue';
import UrlSelect from '@/components/MySelect/url_select.vue';
import { getAreaListDataApi, getBanZuListApi, addXunJianTaskApi } from '@/api';
export default {
props: {
hidden: Function,
refresh: Function,
},
components: { Wraper, RequestSelect, SearchSelect, UrlSelect },
data() {
return {
getAreaListDataApi,
getBanZuListApi,
form: {},
rules: {
inspectionIdList: [{ required: true }],
jobTime: [{ required: true }],
shiftType: [{ required: true }],
routeId: [{ required: true }],
regionId: [{ required: true }],
},
};
},
computed: {
xianLuUrl() {
return this.form.regionId
? `/ranger/inspection/api/v1/routes/regions/${this.form.regionId}/list`
: '';
},
},
methods: {
async submit() {
await this.$refs.form.validate();
return addXunJianTaskApi(this.form);
},
},
};
</script>
import Center from './index.vue';
export default Center;
<template>
<Table
url="/ranger/inspection/api/v1/jobs"
:buttons="buttons"
rowKey="jobId"
:addBtn="addBtn"
:selected.sync="selected"
>
<template #search="{query}">
<my-form-item label="开始时间">
<a-date-picker
class="tw-w-full"
show-time
v-model="query.startTime"
valueFormat="YYYY-MM-DD HH:mm:ss"
/>
</my-form-item>
<my-form-item label="结束时间">
<a-date-picker
class="tw-w-full"
show-time
v-model="query.endTime"
valueFormat="YYYY-MM-DD HH:mm:ss"
/>
</my-form-item>
<my-form-item label="地区">
<RequestSelect
:request="getAreaListDataApi"
v-model="query.regionId"
labelFiled="regionName"
valueFiled="regionId"
/>
</my-form-item>
<my-form-item label="专业">
<UrlSelect
:url="
query.regionId
? `/ranger/inspection/api/v1/specialities/valid/regions/${query.regionId}/list`
: ''
"
v-model="query.specialityId"
labelFiled="specialityName"
valueFiled="specialityId"
/>
</my-form-item>
<my-form-item label="线路">
<UrlSelect
:url="
query.regionId
? `/ranger/inspection/api/v1/routes/regions/${query.regionId}/list`
: ''
"
labelFiled="routeName"
valueFiled="routeId"
v-model="query.routeId"
/>
</my-form-item>
<my-form-item label="班组">
<RequestSelect
:request="getBanZuListApi"
v-model="query.shiftType"
labelFiled="paramName"
valueFiled="paramValue"
/>
</my-form-item>
<my-form-item label="巡检状态">
<RequestSelect
:request="getBusinessListApi"
v-model="query.inspectionState"
labelFiled="paramName"
valueFiled="paramValue"
/>
</my-form-item>
<my-form-item label="任务类型">
<RequestSelect
:request="getTaskTypeApi"
v-model="query.jobType"
labelFiled="paramName"
valueFiled="paramValue"
/>
</my-form-item>
<my-form-item label="巡检人">
<SearchSelect
url="/api/v1/users/searching"
searchField="userName"
v-model="query.lastInspectionStaffId"
labelFiled="userName"
valueFiled="userId"
/>
</my-form-item>
</template>
<template #operation>
<a-button @click="download">导出</a-button>
</template>
<template #drawer="drawer">
<Form v-bind="drawer" />
</template>
<a-table-column title="线路名称" data-index="routeName" />
<a-table-column title="专业" data-index="specialityName" />
<a-table-column title="地区" data-index="regionName" />
<a-table-column title="班组" data-index="shiftTypeName" />
<a-table-column title="任务时间" data-index="jobTime" />
<a-table-column title="巡检状态" data-index="inspectionStateName" />
<a-table-column title="任务类型" data-index="jobTypeName" />
<a-table-column title="巡检开始时间" data-index="inspectionStartTime" />
<a-table-column title="巡检结束时间" data-index="inspectionEndTime" />
<a-table-column title="巡检人" data-index="lastInspectionStaffName" />
</Table>
</template>
<script>
import Table from '@/components/table/table.vue';
import Form from './form.vue';
import UrlSelect from '@/components/MySelect/url_select.vue';
import { getXunJianDownloadUrlApi } from '@/api';
import { downloadFileByUrl } from '@/utils';
import RequestSelect from '@/components/MySelect/RequestSelect.vue';
import SearchSelect from '@/components/MySelect/search_select.vue';
import { getAreaListDataApi, getBusinessListApi, getBanZuListApi, getTaskTypeApi } from '@/api';
export default {
components: { Table, Form, RequestSelect, UrlSelect, SearchSelect },
data() {
return {
getAreaListDataApi,
getBusinessListApi,
getBanZuListApi,
getTaskTypeApi,
addBtn: {
title: '新建临时任务',
},
buttons: [
{
label: '详情',
},
{ label: '进度' },
{ type: 'confirm' },
],
selected: [],
};
},
methods: {
async download() {
if (!this.selected.length) return this.$message.error('请选择需要导出的数据');
const url = await getXunJianDownloadUrlApi(this.selected[0]);
downloadFileByUrl(`${this.$fileUrl}${url}`);
},
},
};
</script>
<template>
<my-table url="table" :search="search" />
</template>
<script>
export default {
data: () => ({
search: [
{
type: 'input',
key: 'name',
label: '所属组织',
options: {
placeholder: '请选择所属组织',
},
},
{
type: 'input',
key: 'address',
label: '地区名称',
},
],
}),
};
</script>
......@@ -55,6 +55,25 @@ const hasAuthorityRoutes = [
},
],
},
{
path: 'ranger',
name: '巡检管理',
component: PageTemplateView,
children: [
{
path: 'task',
name: '任务管理',
component: RouterView,
children: [
{
path: 'center',
name: '任务中心',
component: () => import('@/pages/xunjian/task_managment/center/index'),
},
],
},
],
},
{
path: 'system',
name: '系统管理',
......@@ -131,7 +150,7 @@ const hasAuthorityRoutes = [
},
{
path: 'task',
name: '任务管理',
name: '任务',
component: () => import('@/pages/system/view/task/index.vue'),
},
],
......
......@@ -8,10 +8,9 @@ NProgress.configure({ showSpinner: false });
function hasAuthority(to) {
const { path } = to;
if (globalRoutes.find(m => m.path === path)) return true;
if (globalRoutes.find((m) => m.path === path)) return true;
const { menuList = [] } = getUserInfo();
// return !!menuList.find(i => i.menuUrl === path);
return true;
return !!menuList.find((i) => i.menuUrl === path);
}
/**
......
......@@ -113,9 +113,9 @@ export function arrayToTree(options) {
}
// select组件 数据格式化
export const formatObj = async (ArrObj, obj) => {
export const formatObj = (ArrObj, obj) => {
const newData = [...ArrObj];
const newDataOne = await newData.map(i => {
const newDataOne = newData.map(i => {
const newObj = { ...i };
Object.keys(obj).forEach(key => {
newObj[key] = i[obj[key]];
......@@ -129,3 +129,13 @@ export const isObjEmpty = (data = {}) => {
const arr = Object.keys(data);
return arr.length === 0;
};
/**
* 下载文件
* @param {String} url 下载路径
*/
export function downloadFileByUrl(url) {
const aEle = document.createElement('a');
aEle.setAttribute('download', true);
aEle.setAttribute('href', url);
aEle.click();
}
......@@ -281,7 +281,7 @@ module.exports = {
},
},
postcss: {
plugins: [require('tailwindcss')],
plugins: [require('tailwindcss'), require('autoprefixer')],
},
},
},
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment