Merge pull request 'master' (#361) from master into prod

Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/361
This commit is contained in:
2024-06-19 07:12:19 +00:00
25 changed files with 441 additions and 209 deletions

View File

@@ -7,6 +7,7 @@ export const getCodeImg = () => {
}) })
} }
export const login = (data) => { export const login = (data) => {
return request({ return request({
url: '/auth/login', url: '/auth/login',
@@ -14,6 +15,12 @@ export const login = (data) => {
data data
}) })
} }
export const switchAccount = (userId) => {
return request({
url: `/auth/switch/account/${userId}`,
method: 'post',
})
}
export const getUserInfo = () => { export const getUserInfo = () => {
return request({ return request({

View File

@@ -40,7 +40,7 @@ export const getMenuInfo = (menuId) => {
export const getMenuOpt = (excludeId=0) => { export const getMenuOpt = (excludeId=0) => {
return request({ return request({
url: '/admin/menu/option/role/'+excludeId, url: '/admin/menu/option/'+excludeId,
method: 'get' method: 'get'
}) })
} }

View File

@@ -15,6 +15,12 @@ export const getSubCompOpt = () => {
method: 'get' method: 'get'
}) })
} }
export const getUserAccount = () => {
return request({
url: `/admin/mosr/user/account/list`,
method: 'get'
})
}
// 查询角色信息 // 查询角色信息
export const getRolesOpt = () => { export const getRolesOpt = () => {

View File

@@ -29,7 +29,8 @@
</div> </div>
</div> </div>
<div class="process"> <div class="process">
<operation-render v-if="processViewer && data.operationList && data.operationList.length > 0&&!changeDiagram" :operation-list="data.operationList" <operation-render v-if="processViewer && data.operationList && data.operationList.length > 0&&!changeDiagram"
:operation-list="data.operationList"
:state="data.state"/> :state="data.state"/>
<process-diagram-viewer v-if="processViewer&&changeDiagram" :id-name="idName?idName:type"/> <process-diagram-viewer v-if="processViewer&&changeDiagram" :id-name="idName?idName:type"/>
</div> </div>
@@ -97,9 +98,10 @@ const schema = computed(() => {
component: () => ( component: () => (
<div> <div>
{ {
props.formData.preProcess? props.formData.preProcess ? props.formData.preProcess.map(item => {
<span><a target="_blank" style={{color: '#409EFF', cursor: 'pointer'}} href={props.formData.preProcessBaseUrl + props.formData.preProcess.requestId}>{props.formData.preProcess.requestName}</a> </span> : return <span><a target="_blank" style={{color: '#409EFF', cursor: 'pointer'}}
<span>{'--'}</span> href={props.formData.preProcessBaseUrl + item.requestId}>{item.requestName}</a> </span>
}) : <span>{'--'}</span>
} }
</div> </div>
) )
@@ -138,9 +140,10 @@ const schema = computed(() => {
component: () => ( component: () => (
<div> <div>
{ {
props.formData.preProcess? props.formData.preProcess ? props.formData.preProcess.map(item => {
<span><a target="_blank" style={{color: '#409EFF', cursor: 'pointer'}} href={props.formData.preProcessBaseUrl + props.formData.preProcess.requestId}>{props.formData.preProcess.requestName}</a> </span> : return <span><a target="_blank" style={{color: '#409EFF', cursor: 'pointer'}}
<span>{'--'}</span> href={props.formData.preProcessBaseUrl + item.requestId}>{item.requestName}</a> </span>
}) : <span>{'--'}</span>
} }
</div> </div>
) )

View File

@@ -10,25 +10,26 @@
<el-button @click="getList()">搜索</el-button> <el-button @click="getList()">搜索</el-button>
</template> </template>
</el-input> </el-input>
<el-button v-if="isChooseAll" @click="chooseAll">全选</el-button> <!-- <el-button v-if="isChooseAll" @click="chooseAll">全选</el-button>-->
<el-button v-else @click="cancelAll">不全选</el-button> <!-- <el-button v-else @click="cancelAll">不全选</el-button>-->
<!-- <el-checkbox-group v-model="checkBtn" @change="handleChange">--> <!-- <el-checkbox-group v-model="checkBtn" @change="handleChange">-->
<!-- <el-checkbox label="全选" value="1" />--> <!-- <el-checkbox label="全选" value="1" />-->
<!-- <el-checkbox label="Option B" value="Value B" />--> <!-- <el-checkbox label="Option B" value="Value B" />-->
<!-- <el-checkbox label="Option C" value="Value C" />--> <!-- <el-checkbox label="Option C" value="Value C" />-->
<!-- <el-checkbox label="disabled" value="Value disabled" disabled />--> <!-- <el-checkbox label="disabled" value="Value disabled" disabled />-->
<!-- <el-checkbox--> <!-- <el-checkbox-->
<!-- label="selected and disabled"--> <!-- label="selected and disabled"-->
<!-- value="Value selected and disabled"--> <!-- value="Value selected and disabled"-->
<!-- disabled--> <!-- disabled-->
<!-- />--> <!-- />-->
<!-- </el-checkbox-group>--> <!-- </el-checkbox-group>-->
<!-- 人员选择 --> <!-- 人员选择 -->
<el-empty :image-size="100" description="似乎没有数据" v-show="dataList.length === 0"/> <el-empty :image-size="100" description="似乎没有数据" v-show="dataList.length === 0"/>
<el-scrollbar style="height:87%;"> <el-scrollbar style="height:87%;">
<div class="tree"> <div class="tree">
<el-tree :data="dataList" ref="tree" :props="defaultProps" empty-text="" node-key="value" <el-tree :data="dataList" ref="tree" :props="defaultProps" empty-text="" node-key="value"
default-expand-all :show-checkbox="showCheckbox" highlight-current :default-checked-keys="defaultChecked" default-expand-all :show-checkbox="showCheckbox" highlight-current
:default-checked-keys="defaultChecked"
:check-strictly="true" @node-click="(node,check)=>handle(node,check)" :check-strictly="true" @node-click="(node,check)=>handle(node,check)"
@check-change="handleChange" :filter-node-method="filterNode"> @check-change="handleChange" :filter-node-method="filterNode">
<template #default="{ node, data }"> <template #default="{ node, data }">
@@ -85,7 +86,7 @@ const props = defineProps({
type: Boolean type: Boolean
} }
}); });
const checkBtn=ref(1) const checkBtn = ref(1)
const isChooseAll = ref(true); const isChooseAll = ref(true);
let selectItem = reactive({ let selectItem = reactive({
type: -1, type: -1,
@@ -117,25 +118,31 @@ const _value = computed({
watch(() => filterText.value, (newVal) => { watch(() => filterText.value, (newVal) => {
tree.value.filter(newVal); tree.value.filter(newVal);
}); });
const matterTree=(list)=>{ const matterTree = (list) => {
list.forEach(item=>{ list.forEach(item => {
tree.value.setChecked(item,true) tree.value.setChecked(item, true)
if(item.children!==undefined){ if (item.children !== undefined) {
matterTree(item.children) matterTree(item.children)
} }
}) })
} }
const cancelAll=()=>{ const cancelAll = () => {
isChooseAll.value=true isChooseAll.value = true
tree.value.setCheckedNodes([]) tree.value.setCheckedNodes([])
} }
const chooseAll=()=>{ const chooseAll = () => {
isChooseAll.value=false isChooseAll.value = false
matterTree(dataList.value) matterTree(dataList.value)
} }
const getList = () => { const getList = () => {
getSubCompOpt().then(res => { getSubCompOpt().then(res => {
dataList.value = res.data; dataList.value = [
{
label: "全选",
value: -1,
children: res.data
}
]
}); });
}; };
@@ -149,48 +156,50 @@ const filterNode = (value, data) => {
const show = () => { const show = () => {
//用于弹开部门选择 //用于弹开部门选择
visible.value = true; visible.value = true;
selectList.value=_value.value selectList.value = _value.value
defaultChecked.value=_value.value.map(item=>item.value) defaultChecked.value = _value.value.map(item => item.value)
getList() getList()
}; };
const handleChange = (data, checked) => { const handleChange = (data, checked) => {
// 左侧有选择框 if(data.value==-1&&checked){
// if (props.showCheckbox) { chooseAll()
// 左侧有选择框 + 多选 }else if(data.value==-1&&!checked){
if (props.multiple) { cancelAll()
//不添加重复的数据到右边 }
for (let i = 0; i < selectList.value.length; i++) { // 左侧有选择框 + 多选
if (selectList.value[i].value === data.value) { if (props.multiple) {
selectList.value.splice(i, 1); //不添加重复的数据到右边
break; for (let i = 0; i < selectList.value.length; i++) {
} if (selectList.value[i].value === data.value) {
} selectList.value.splice(i, 1);
if (checked) { break;
// if (data.children === undefined) {
selectList.value.push(data);
// }
} else if (data === "1") {
tree.value.setCheckedKeys([]);
selectList.value = [];
}
} else {// 左侧有选择框 + 单选
//不添加重复的数据到右边
for (let i = 0; i < selectList.value.length; i++) {
if (selectList.value[i].value === data.value) {
selectList.value.splice(i, 1);
break;
}
}
if (checked) {
tree.value.setCheckedNodes([data]);
// this.$refs.tree.setCheckedKeys([]);
selectList.value = [data];
} else if (data === "1") {
selectList.value = [];
tree.value.setCheckedKeys([]);
} }
} }
// } if (checked) {
// if (data.children === undefined) {
selectList.value.push(data);
// }
} else if (data === "1") {
tree.value.setCheckedKeys([]);
selectList.value = [];
}
} else {// 左侧有选择框 + 单选
//不添加重复的数据到右边
for (let i = 0; i < selectList.value.length; i++) {
if (selectList.value[i].value === data.value) {
selectList.value.splice(i, 1);
break;
}
}
if (checked) {
tree.value.setCheckedNodes([data]);
// this.$refs.tree.setCheckedKeys([]);
selectList.value = [data];
} else if (data === "1") {
selectList.value = [];
tree.value.setCheckedKeys([]);
}
}
}; };
const handle = (node, check) => { const handle = (node, check) => {
if (check.isLeaf !== false) { if (check.isLeaf !== false) {
@@ -215,7 +224,7 @@ const noSelected = (selectItem) => {
//左侧无选择框时,右侧显示× //左侧无选择框时,右侧显示×
for (let i = 0; i < selectList.value.length; i++) { for (let i = 0; i < selectList.value.length; i++) {
if (selectList.value[i].value === selectItem.value) { if (selectList.value[i].value === selectItem.value) {
tree.value.setChecked(selectList.value[i].value,false); tree.value.setChecked(selectList.value[i].value, false);
selectList.value.splice(i, 1); selectList.value.splice(i, 1);
break; break;
} }

View File

@@ -4,10 +4,12 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="前置流程" :required="preProcessRequired" prop="requestName"> <el-form-item label="前置流程" :required="preProcessRequired" prop="requestName">
<a :href="localFormData.preProcess?.baseUrl" target="_blank" <div v-for="item in localFormData.preProcess" :key="item.requestId">
style="color: #2a99ff;margin-right: 10px;cursor: pointer">{{ localFormData.preProcess?.requestName }}</a> <a :href="item.baseUrl" target="_blank"
style="color: #2a99ff;margin-right: 10px;cursor: pointer">{{ item.requestName }}</a>
</div>
<el-button color="#DED0B2" @click="handleShowPreTable"> <el-button color="#DED0B2" @click="handleShowPreTable">
{{ localFormData.preProcess?.requestName ? '更改' : '请选择' }} {{ localFormData.preProcess ? '更改' : '请选择' }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -32,29 +34,30 @@
</div> </div>
</div> </div>
<div class="process"> <div class="process">
<operation-render v-if="mode === 'resubmit'&&!changeDiagram" :operation-list="data.operationList" :state="data.state"/> <operation-render v-if="mode === 'resubmit'&&!changeDiagram" :operation-list="data.operationList"
:state="data.state"/>
<process-diagram-viewer mode="view" :idName="title" v-if="processDiagramViewer&&changeDiagram"/> <process-diagram-viewer mode="view" :idName="title" v-if="processDiagramViewer&&changeDiagram"/>
</div> </div>
</div> </div>
</div> </div>
<!-- <div v-if="changeDiagram">--> <!-- <div v-if="changeDiagram">-->
<!-- <div class="approval-record">--> <!-- <div class="approval-record">-->
<!-- <div class="approval-title">--> <!-- <div class="approval-title">-->
<!-- <baseTitle title="流程图"></baseTitle>--> <!-- <baseTitle title="流程图"></baseTitle>-->
<!-- <div class="diagram">--> <!-- <div class="diagram">-->
<!--&lt;!&ndash; <div class="base-title">流程图</div>&ndash;&gt;--> <!--&lt;!&ndash; <div class="base-title">流程图</div>&ndash;&gt;-->
<!--&lt;!&ndash; <el-switch&ndash;&gt;--> <!--&lt;!&ndash; <el-switch&ndash;&gt;-->
<!--&lt;!&ndash; v-model="changeDiagram"&ndash;&gt;--> <!--&lt;!&ndash; v-model="changeDiagram"&ndash;&gt;-->
<!--&lt;!&ndash; style="&#45;&#45;el-switch-on-color: #13ce66; &#45;&#45;el-switch-off-color:#BEA266"&ndash;&gt;--> <!--&lt;!&ndash; style="&#45;&#45;el-switch-on-color: #13ce66; &#45;&#45;el-switch-off-color:#BEA266"&ndash;&gt;-->
<!--&lt;!&ndash; />&ndash;&gt;--> <!--&lt;!&ndash; />&ndash;&gt;-->
<!-- </div>--> <!-- </div>-->
<!-- </div>--> <!-- </div>-->
<!-- </div>--> <!-- </div>-->
<!-- <div class="process">--> <!-- <div class="process">-->
<!-- <process-diagram-viewer mode="view" :idName="title" v-if="processDiagramViewer"/>--> <!-- <process-diagram-viewer mode="view" :idName="title" v-if="processDiagramViewer"/>-->
<!-- </div>--> <!-- </div>-->
<!-- </div>--> <!-- </div>-->
<div class="oper-page-btn"> <div class="oper-page-btn">
<el-button color="#DED0B2" v-if="mode === 'submit'" @click="handleSubmit">提交</el-button> <el-button color="#DED0B2" v-if="mode === 'submit'" @click="handleSubmit">提交</el-button>
<el-button color="#DED0B2" v-else-if="mode === 'resubmit'" @click="handleSubmit">重新提交</el-button> <el-button color="#DED0B2" v-else-if="mode === 'resubmit'" @click="handleSubmit">重新提交</el-button>
@@ -71,7 +74,9 @@
<el-button @click="handleReset">重置</el-button> <el-button @click="handleReset">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table :data="preProcessList" stripe v-loading="loading"> <el-table :data="preProcessList" stripe v-loading="loading"
@select="handleSelect" row-key="requestId">
<el-table-column type="selection" width="55" :reserve-selection="true"/>
<el-table-column prop="requestId" label="请求id"></el-table-column> <el-table-column prop="requestId" label="请求id"></el-table-column>
<el-table-column prop="requestName" label="请求名称"></el-table-column> <el-table-column prop="requestName" label="请求名称"></el-table-column>
<el-table-column prop="lastOperatorName" label="最后操作人名称"></el-table-column> <el-table-column prop="lastOperatorName" label="最后操作人名称"></el-table-column>
@@ -81,15 +86,17 @@
<el-table-column prop="createTime" label="创建时间"></el-table-column> <el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<div style="display: flex;align-items: center"> <!-- <el-button type="primary" @click="choosePreProcess(scope.row)" link>选择</el-button>-->
<el-button type="primary" @click="chooseProProcess(scope.row)" link>选择</el-button> <a :href="scope.row.baseUrl" target="_blank" style="color: #2a99ff;margin-left: 10px">查看流程</a>
<a :href="scope.row.baseUrl" target="_blank" style="color: #2a99ff;margin-left: 10px">查看流程</a>
</div>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<paging :current-page="pageInfo.pageNum" :page-size="pageInfo.pageSize" :page-sizes="[10, 20, 30, 40,50]" <paging :current-page="pageInfo.pageNum" :page-size="pageInfo.pageSize" :page-sizes="[10, 20, 30, 40,50]"
:total="total" @changeSize="handleSizeChange" @goPage="handleCurrentChange"/> :total="total" @changeSize="handleSizeChange" @goPage="handleCurrentChange"/>
<div class="oper">
<el-button color="#DED0B2" @click="choosePreProcess">确定</el-button>
<el-button @click="handleCancel">取消</el-button>
</div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
@@ -163,11 +170,13 @@ const tagsViewStore = useTagsView()
const processStore = useProcessStore() const processStore = useProcessStore()
const otherFileList = ref([]) const otherFileList = ref([])
const localFormData = ref({ const localFormData = ref({
preProcess: { preProcess: [
requestId: null, // {
requestName: '', // requestId: null,
baseUrl: '' // requestName: '',
} // baseUrl: ''
// }
]
}) })
const attachment = ref() const attachment = ref()
const deploymentData = ref({}) const deploymentData = ref({})
@@ -177,7 +186,24 @@ const loading = ref(false)
const processDiagramViewer = ref(false) const processDiagramViewer = ref(false)
const name = ref(router.currentRoute.value.name) const name = ref(router.currentRoute.value.name)
const deploymentId = ref() const deploymentId = ref()
const selectRows = ref([])
const projectId = ref(route.query.projectId) const projectId = ref(route.query.projectId)
const getPreProcessUrl = (list) => {
// list.map(item => {
// item.baseUrl = getPreProcessUrl(item.preProcess)
// })
// let baseUrl=
// list.forEach(item => {
// baseUrl=item.baseUrl
// })
// return baseUrl
}
const handleSelect = async (selection) => {
selectRows.value = selection
}
const handleCancel = () => {
showPreTable.value = false
}
const searchPreProcess = () => { const searchPreProcess = () => {
getPreProcessList() getPreProcessList()
@@ -207,12 +233,19 @@ const getPreProcessList = () => {
preProcessList.value = currentList.value.slice(0, 10) preProcessList.value = currentList.value.slice(0, 10)
}) })
} }
const chooseProProcess = (item) => { const choosePreProcess = () => {
localFormData.value.preProcess = { let preProcessObj = {}
requestId: item.requestId, let preProcessArray = []
requestName: item.requestName, selectRows.value.forEach((item) => {
baseUrl: item.baseUrl preProcessObj = {
} requestId: item.requestId,
requestName: item.requestName,
baseUrl: item.baseUrl
}
preProcessArray.push(preProcessObj)
})
localFormData.value.preProcess = preProcessArray
console.log('localFormData.value.preProcess', localFormData.value.preProcess)
showPreTable.value = false showPreTable.value = false
} }
@@ -252,11 +285,11 @@ const compositeParam = (item) => {
} }
} }
const getAttachment = (val) => { const getAttachment = (val) => {
console.log('上传文件getAttachment', val) // console.log('上传文件getAttachment', val)
localFormData.value.singleFile = compositeParam(val) localFormData.value.singleFile = compositeParam(val)
} }
const getOtherFile = (val) => { const getOtherFile = (val) => {
console.log('上传文件getOtherFile', val) // console.log('上传文件getOtherFile', val)
showTable.value = false showTable.value = false
let fileObj = compositeParam(val) let fileObj = compositeParam(val)
otherFileList.value.push(fileObj) otherFileList.value.push(fileObj)
@@ -271,7 +304,7 @@ const getFileParam = (item) => {
} }
} }
const handleSubmit = async () => { const handleSubmit = async () => {
if(deploymentData.value.deploymentName==='重大项目立项'||deploymentData.value.deploymentName==='重大项目验收'){ if (deploymentData.value.deploymentName === '重大项目立项' || deploymentData.value.deploymentName === '重大项目验收') {
if (localFormData.value.preProcess === undefined) { if (localFormData.value.preProcess === undefined) {
ElNotification({ ElNotification({
title: '提示', title: '提示',
@@ -377,7 +410,7 @@ const init = async () => {
let data = res.data let data = res.data
deploymentId.value = data.deploymentId deploymentId.value = data.deploymentId
deploymentData.value = data deploymentData.value = data
preProcessRequired.value = data.deploymentName === '重大项目立项'||data.deploymentName === '重大项目验收'; preProcessRequired.value = data.deploymentName === '重大项目立项' || data.deploymentName === '重大项目验收';
processStore.setDesign(data) processStore.setDesign(data)
processStore.runningList.value = data.runningList; processStore.runningList.value = data.runningList;
processStore.endList.value = data.endList; processStore.endList.value = data.endList;
@@ -406,5 +439,9 @@ onMounted(async () => {
</script> </script>
<style scoped> <style scoped>
.oper {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style> </style>

View File

@@ -144,7 +144,7 @@ const localData = reactive({
checkGroup: [] checkGroup: []
}) })
const emits = defineEmits(['headBtnClick', 'selectionChange', 'rowClick', 'rowDblclick', 'getBaseQuery', 'cellClick']) const emits = defineEmits(['headBtnClick', 'selectionChange', 'rowClick', 'rowDblclick', 'getBaseQuery', 'cellClick', 'getTotal'])
const handleClickBtns = (key) => { const handleClickBtns = (key) => {
emits('headBtnClick', key) emits('headBtnClick', key)
@@ -211,6 +211,7 @@ const getList = async () => {
} }
if(data.total){ if(data.total){
localData.total = data.total localData.total = data.total
emits('getTotal', localData.total)
} }
localData.loading = false localData.loading = false
} else { } else {

View File

@@ -6,13 +6,47 @@
<!-- <bell-socket/>--> <!-- <bell-socket/>-->
<div class="user-box"> <div class="user-box">
<div> <div>
<!-- <img :src="userInfo.avatar" alt="" @click.stop="handleVisitedP">--> <el-avatar>{{ userInfo.nickName }}</el-avatar>
<span @click.stop="handleVisitedP">欢迎回来{{ userInfo.userName }}</span> <div @click.stop="handleVisitedP">{{ userInfo.nickName }}
<el-icon style="margin-left: 5px">
<ArrowDownBold/>
</el-icon>
</div>
</div> </div>
<div class="person" v-if="visitedP"> <div class="person" v-if="visitedP">
<ul> <ul>
<li @click="handleToAuth">个人中心</li> <li>主次账号切换</li>
<li @click="handleLogout">退出登录</li> <li class="avatar-li" v-for="item in accountList" @click="accountChange(item.userId)">
<el-avatar>{{ item.nickName }}</el-avatar>
<div class="right-li">
<div class="name-line">
<span v-if="item.accountType==='0'" class="zhu"></span>
<span class="nickName">{{ item.nickName }}</span>
<span>{{ item.jobActivityDesc }}</span>
</div>
<div>
<span :title="item.companyName+'/'+item.departmentName">{{ item.companyName }}/{{ item.departmentName }}</span>
</div>
</div>
<div>
<el-icon color="#3f89dc" size="20" v-if="item.current">
<SuccessFilled/>
</el-icon>
</div>
<!-- <li v-for="item in accountList" :label="item.userName" :value="item.userId"/>-->
</li>
<!-- <li @click="handleToAuth">-->
<!-- <el-icon color="gray" size="20" style="margin-right: 5px">-->
<!-- <UserFilled/>-->
<!-- </el-icon>-->
<!-- 个人中心-->
<!-- </li>-->
<li @click="handleLogout">
<el-icon color="gray" size="20" style="margin-right: 5px">
<SwitchButton/>
</el-icon>
退出登录
</li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -26,13 +60,18 @@ import Breadcrumb from './Breadcrumb.vue';
import Hamburger from './Hamburger.vue'; import Hamburger from './Hamburger.vue';
import {useAuthStore} from '@/stores/userstore.js' import {useAuthStore} from '@/stores/userstore.js'
import {usePermisstionStroe} from '@/stores/permisstion' import {usePermisstionStroe} from '@/stores/permisstion'
import { useTagsView } from '@/stores/tagsview'; import {useTagsView} from '@/stores/tagsview';
import {getUserAccount} from "@/api/user/user";
import {switchAccount} from "@/api/login";
import {setToken} from "../../utils/auth";
const authStore = useAuthStore() const authStore = useAuthStore()
const permisstionStore = usePermisstionStroe() const permisstionStore = usePermisstionStroe()
const tagsViewStore = useTagsView() const tagsViewStore = useTagsView()
const userInfo = ref({}) const userInfo = ref({})
const visitedP = ref(false) const visitedP = ref(false)
const accountList = ref([])
const selectUserId = ref()
const router = useRouter() const router = useRouter()
onMounted(() => { onMounted(() => {
setUserInfo() setUserInfo()
@@ -48,7 +87,26 @@ const nullBlockClick = () => {
visitedP.value = false visitedP.value = false
} }
const handleVisitedP = () => { const handleVisitedP = () => {
visitedP.value = !visitedP.value getUserAccount().then(res => {
console.log(res)
accountList.value = res.data
nextTick(() => {
visitedP.value = !visitedP.value
})
})
}
const accountChange = (userId) => {
switchAccount(userId).then(res => {
if (res.code == 1000) {
visitedP.value = !visitedP.value
authStore.userLogout()
setToken(res.data)
router.push('/')
location.reload()
visitedP.value = false
}
})
} }
const handleToAuth = () => { const handleToAuth = () => {
@@ -63,9 +121,18 @@ const handleLogout = () => {
tagsViewStore.removeAllTagView() tagsViewStore.removeAllTagView()
router.push('/login') router.push('/login')
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-avatar--circle) {
margin-right: 10px;
background-color: #8a7243;
white-space: nowrap;
/* width: 30px; */
overflow: hidden;
}
.navbar { .navbar {
height: 65px; height: 65px;
padding: 0 15px 0 0; padding: 0 15px 0 0;
@@ -90,8 +157,9 @@ const handleLogout = () => {
display: flex; display: flex;
align-items: center; align-items: center;
> span { > div {
margin-left: 5px; display: flex;
align-items: center;
} }
img { img {
@@ -105,24 +173,82 @@ const handleLogout = () => {
.person { .person {
font-size: 14px;
color: #666666;
position: absolute; position: absolute;
width: 80px; width: 280px;
right: 0; right: 0;
z-index: 300; z-index: 300;
bottom: -70px; top: 54px;
padding: 10px 5px; padding: 5px 0;
border-radius: 4px; border-radius: 4px;
background-color: #fff; background-color: #fff;
box-shadow: 2px 2px 2px 1px rgb(171, 167, 167); box-shadow: 2px 2px 2px 1px rgb(171, 167, 167);
li { .avatar-li {
text-align: center; display: flex;
font-size: 14px; height: 60px;
line-height: 1.5;
cursor: pointer;
.right-li {
color: #909090;
display: flex;
flex-direction: column;
.name-line {
margin-bottom: 5px;
.zhu {
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
background-color: #fa0;
color: #fff;
text-align: center;
}
.nickName {
color: #4d7ad8;
}
> span {
margin-right: 5px;
}
}
> div:last-child {
width: 194px;
-webkit-line-clamp: 1;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
li {
&:hover { &:hover {
color: #79bbff; color: #666666 !important;
background-color: #eaeaea;
}
&:first-child:hover {
background-color: #fff;
}
padding: 0 10px;
height: 28px;
display: flex;
align-items: center;
text-align: left;
font-size: 14px;
cursor: pointer;
border-bottom: 1px solid #e6e6e6;
&:last-child {
border-bottom: none;
} }
} }
} }

View File

@@ -34,7 +34,8 @@ const searchConfig = reactive([
valueFormat: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'YYYY-MM-DD HH:mm:ss',
}, },
colProps: {} colProps: {}
}, { },
{
label: '项目费用', label: '项目费用',
prop: 'projectCost', prop: 'projectCost',
component: shallowRef(fvSelect), component: shallowRef(fvSelect),
@@ -44,16 +45,17 @@ const searchConfig = reactive([
filterable: true, filterable: true,
cacheKey: 'project_cost', cacheKey: 'project_cost',
} }
}, { },
label: '项目阶段', {
label: '研发阶段',
prop: 'researchStage', prop: 'researchStage',
component: shallowRef(fvSelect), component: shallowRef(fvSelect),
props: { props: {
placeholder: '请选择项目阶段查询', placeholder: '请选择研发阶段查询',
clearable: true, clearable: true,
filterable: true, filterable: true,
checkStrictly: true, checkStrictly: true,
cacheKey: 'research_stage', cacheKey: 'fee_stage',
} }
}, },
{ {
@@ -113,7 +115,7 @@ const tableConfig = reactive({
}, },
{ {
prop: 'researchStage', prop: 'researchStage',
label: '项目阶段', label: '研发阶段',
align: 'center', align: 'center',
showOverflowTooltip: false, showOverflowTooltip: false,
currentRender: ({row, index}) => { currentRender: ({row, index}) => {
@@ -124,6 +126,11 @@ const tableConfig = reactive({
} }
} }
}, },
{
label: '摘要',
prop: 'digest',
align: 'center'
},
{ {
prop: 'afterTax', prop: 'afterTax',
label: '税后余额(元)', label: '税后余额(元)',

View File

@@ -102,7 +102,7 @@ const tableConfig = reactive({
}, },
{ {
prop: 'researchStage', prop: 'researchStage',
label: '项目阶段', label: '研发阶段',
align: 'center', align: 'center',
currentRender: ({row, index}) => { currentRender: ({row, index}) => {
if (row.researchStage&&row.researchStage !== null&&row.researchStage!==undefined) { if (row.researchStage&&row.researchStage !== null&&row.researchStage!==undefined) {

View File

@@ -53,7 +53,8 @@ const searchConfig = reactive([
filterable: true, filterable: true,
checkStrictly: true checkStrictly: true
} }
}, { },
{
label: '分摊月份', label: '分摊月份',
prop: 'apportionmentMonth', prop: 'apportionmentMonth',
component: 'el-date-picker', component: 'el-date-picker',

View File

@@ -34,8 +34,9 @@
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="所属公司" prop="companyIds" class="tree-select"> <el-form-item label="所属公司" prop="companyIds" class="tree-select">
<div>{{ getName(companyList) }}</div> <div v-if="route.query.id">{{selectedCompanyList}}</div>
<el-button color="#DED0B2" @click="showCompany">{{ companyList.length === 0 ? '请选择所属公司' : '更改' }} <div v-else>{{ getName(selectedCompanyList) }}</div>
<el-button color="#DED0B2" @click="showCompany">{{ selectedCompanyList.length === 0 ? '请选择所属公司' : '更改' }}
</el-button> </el-button>
<!-- <el-tree-select v-model="formData.companyIds" :data="companyOption"--> <!-- <el-tree-select v-model="formData.companyIds" :data="companyOption"-->
<!-- filterable clearable :check-strictly="true" multiple/>--> <!-- filterable clearable :check-strictly="true" multiple/>-->
@@ -96,7 +97,7 @@
<el-button color="#DED0B2" v-else @click="handleResubmit">重新提交</el-button> <el-button color="#DED0B2" v-else @click="handleResubmit">重新提交</el-button>
<el-button @click="handleBack">返回</el-button> <el-button @click="handleBack">返回</el-button>
</div> </div>
<company-picker :multiple="true" ref="companyRef" title="请选择所属公司" v-model:value="companyList" @ok="selected"/> <company-picker :multiple="true" ref="companyRef" title="请选择所属公司" @ok="selected"/>
</div> </div>
</template> </template>
@@ -119,11 +120,13 @@ import {getSubCompOpt} from '@/api/user/user.js'
import {useTagsView} from '@/stores/tagsview.js' import {useTagsView} from '@/stores/tagsview.js'
import {getFundOption} from "@/api/special-fund"; import {getFundOption} from "@/api/special-fund";
import CompanyPicker from "@/components/DetailComponent/CompanyPicker.vue"; import CompanyPicker from "@/components/DetailComponent/CompanyPicker.vue";
import {matterTree} from "@/utils/matterTree";
import {useCacheStore} from '@/stores/cache.js' import {useCacheStore} from '@/stores/cache.js'
const cacheStore = useCacheStore() const cacheStore = useCacheStore()
const companyRef = ref() const companyRef = ref()
const companyList = ref([]) const selectedCompanyList = ref([])
// const companyList = ref([])
const changeDiagram = ref(false) const changeDiagram = ref(false)
const tagsViewStore = useTagsView() const tagsViewStore = useTagsView()
const authStore = useAuthStore() const authStore = useAuthStore()
@@ -152,8 +155,9 @@ const showTable = ref(true)
const processStore = useProcessStore() const processStore = useProcessStore()
const processInstanceData = ref() const processInstanceData = ref()
const formPermMap = ref(new Map()); const formPermMap = ref(new Map());
const companyNameArray = ref([])
const rules = reactive({ const rules = reactive({
requirementName: [{required: true, message: '请输入需求名称', trigger: 'blur'}], requirementName: [{required: true, message: '请输入征集名称', trigger: 'blur'}],
companyIds: [{required: true, message: '请选择所属公司', trigger: 'blur'}], companyIds: [{required: true, message: '请选择所属公司', trigger: 'blur'}],
collectType: [{required: true, message: '请选择征集类型', trigger: 'blur'}], collectType: [{required: true, message: '请选择征集类型', trigger: 'blur'}],
deadline: [{required: true, message: '请选择截止时间', trigger: 'blur'}], deadline: [{required: true, message: '请选择截止时间', trigger: 'blur'}],
@@ -203,6 +207,15 @@ const tableConfig = reactive({
} }
] ]
}) })
const getCompanyOptionItem = (val) => {
if (val !== undefined) {
val.forEach(item => {
matterTree(companyNameArray.value, companyOption.value, item)
})
}
return companyNameArray.value.join('');
}
const getName = (list) => { const getName = (list) => {
return list.map(item => item.label).join('') return list.map(item => item.label).join('')
} }
@@ -210,16 +223,10 @@ const showCompany = () => {
companyRef.value.show() companyRef.value.show()
} }
const selected = (select) => { const selected = (select) => {
let companyInfoList = []
for (let val of select) { for (let val of select) {
let companyInfo = {
value: val.value,
label: val.label
}
companyInfoList.push(companyInfo)
formData.value.companyIds.push(val.value) formData.value.companyIds.push(val.value)
} }
companyList.value = companyInfoList selectedCompanyList.value = select
} }
const checkFormPrem = (formKey) => { const checkFormPrem = (formKey) => {
if (formPermMap.value.hasOwnProperty(formKey)) { if (formPermMap.value.hasOwnProperty(formKey)) {
@@ -310,7 +317,6 @@ const init = async () => {
}) })
} }
const submitParam = (item) => { const submitParam = (item) => {
console.log('item..', item.companyIds)
if (item.companyIds.length === 0) { if (item.companyIds.length === 0) {
ElNotification({ ElNotification({
title: '提示', title: '提示',
@@ -377,6 +383,7 @@ const handleResubmit = () => {
const getDetailInfo = async () => { const getDetailInfo = async () => {
getFormInfo(route.query.id).then(res => { getFormInfo(route.query.id).then(res => {
if (res.code === 1000) { if (res.code === 1000) {
selectedCompanyList.value = getCompanyOptionItem(res.data.companyIds)
formData.value = res.data formData.value = res.data
showTinymce.value = false showTinymce.value = false
showTable.value = false showTable.value = false

View File

@@ -36,7 +36,7 @@ const searchConfig = reactive([
placeholder: '请选择征集类型', placeholder: '请选择征集类型',
clearable: true, clearable: true,
filterable: true, filterable: true,
cacheKey: 'todo_type' cacheKey: 'collect_type'
} }
}, },
{ {
@@ -61,10 +61,10 @@ const auths = {
} }
const tableConfig = reactive({ const tableConfig = reactive({
columns: [ columns: [
{ // {
type: 'selection', // type: 'selection',
prop: 'selection' // prop: 'selection'
}, // },
{ {
prop: 'requirementName', prop: 'requirementName',
label: '征集名称', label: '征集名称',

View File

@@ -10,10 +10,10 @@ const tableIns = ref()
const router = useRouter() const router = useRouter()
const searchConfig = reactive([ const searchConfig = reactive([
{ {
label: '需求名称', label: '征集名称',
prop: 'requirementName', prop: 'requirementName',
props: { props: {
placeholder: '请输入需求名称', placeholder: '请输入征集名称',
clearable: true, clearable: true,
checkStrictly: true checkStrictly: true
}, },
@@ -34,7 +34,7 @@ const searchConfig = reactive([
}, },
{ {
label: '研发主体', label: '研发主体',
prop: 'productMainBody', prop: 'rdSubject',
component: shallowRef(fvSelect), component: shallowRef(fvSelect),
props: { props: {
placeholder: '请选择研发主体', placeholder: '请选择研发主体',
@@ -46,9 +46,14 @@ const searchConfig = reactive([
}, },
// { // {
// label: '项目影响', // label: '项目影响',
// prop: 'projectEffect', // prop: 'projectImpact',
// component: shallowRef(fvSelect), // component: shallowRef(fvSelect),
// props: {}, // props: {
// placeholder: '请选择项目影响',
// cacheKey: 'project_impact',
// clearable: true,
// filterable: true
// },
// colProps: {} // colProps: {}
// }, // },
// { // {
@@ -78,10 +83,10 @@ const auths = {
} }
const tableConfig = reactive({ const tableConfig = reactive({
columns: [ columns: [
{ // {
type: 'selection', // type: 'selection',
prop: 'selection' // prop: 'selection'
}, // },
{ {
prop: 'requirementName', prop: 'requirementName',
label: '征集名称', label: '征集名称',

View File

@@ -60,11 +60,11 @@ const searchConfig = reactive([
} }
}, },
{ {
label: '项目阶段', label: '研发阶段',
prop: 'researchStage', prop: 'researchStage',
component: shallowRef(fvSelect), component: shallowRef(fvSelect),
props: { props: {
placeholder: '请选择项目阶段查询', placeholder: '请选择研发阶段查询',
clearable: true, clearable: true,
filterable: true, filterable: true,
checkStrictly: true, checkStrictly: true,
@@ -123,7 +123,7 @@ const tableConfig = reactive({
}, },
{ {
prop: 'researchStage', prop: 'researchStage',
label: '项目阶段', label: '研发阶段',
align: 'center', align: 'center',
showOverflowTooltip: false, showOverflowTooltip: false,
currentRender: ({row, index}) => { currentRender: ({row, index}) => {

View File

@@ -93,7 +93,7 @@ const searchConfig = reactive([
component: 'el-date-picker', component: 'el-date-picker',
props: { props: {
clearable: true, clearable: true,
type: 'datetimerange', type: 'daterange',
startPlaceholder: '开始日期', startPlaceholder: '开始日期',
endPlaceholder: '结束日期', endPlaceholder: '结束日期',
valueFormat: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'YYYY-MM-DD HH:mm:ss',
@@ -136,10 +136,10 @@ const searchConfig = reactive([
const tableIns = ref() const tableIns = ref()
const tableConfig = reactive({ const tableConfig = reactive({
columns: [ columns: [
{ // {
type: 'selection', // type: 'selection',
prop: 'selection' // prop: 'selection'
}, // },
{ {
prop: 'projectName', prop: 'projectName',
label: '项目名称', label: '项目名称',
@@ -183,7 +183,7 @@ const tableConfig = reactive({
}, },
{ {
prop: 'researchStage', prop: 'researchStage',
label: '项目阶段', label: '研发阶段',
align: 'center', align: 'center',
showOverflowTooltip: false, showOverflowTooltip: false,
currentRender: ({row, index}) => { currentRender: ({row, index}) => {

View File

@@ -31,10 +31,10 @@
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="researchStage" label="项目阶段"> <el-table-column prop="researchStage" label="研发阶段">
<template #default="scope"> <template #default="scope">
<el-form-item prop="researchStage" :rules="scope.row.researchStage?'1':rules.researchStage"> <el-form-item prop="researchStage" :rules="scope.row.researchStage?'1':rules.researchStage">
<el-select v-model="scope.row.researchStage" placeholder="请选择项目阶段" clearable filterable> <el-select v-model="scope.row.researchStage" placeholder="请选择研发阶段" clearable filterable>
<el-option <el-option
v-for="item in cacheStore.getDict('fee_stage')" v-for="item in cacheStore.getDict('fee_stage')"
:key="item.value" :key="item.value"
@@ -115,7 +115,7 @@ const baseForm = ref()
const rules = reactive({ const rules = reactive({
time: [{required: true, message: '请选择时间', trigger: 'blur'}], time: [{required: true, message: '请选择时间', trigger: 'blur'}],
projectCost: [{required: true, message: '请输入项目费用', trigger: 'blur'}], projectCost: [{required: true, message: '请输入项目费用', trigger: 'blur'}],
researchStage: [{required: true, message: '请输入项目阶段', trigger: 'blur'}], researchStage: [{required: true, message: '请输入研发阶段', trigger: 'blur'}],
digest: [{required: true, message: '请输入摘要', trigger: 'blur'}], digest: [{required: true, message: '请输入摘要', trigger: 'blur'}],
afterTax: [{required: true, message: '请输入税后余额', trigger: 'blur'}] afterTax: [{required: true, message: '请输入税后余额', trigger: 'blur'}]
}) })

View File

@@ -91,7 +91,7 @@ const searchConfig = reactive([
component: 'el-date-picker', component: 'el-date-picker',
props: { props: {
clearable: true, clearable: true,
type: 'datetimerange', type: 'daterange',
startPlaceholder: '开始日期', startPlaceholder: '开始日期',
endPlaceholder: '结束日期', endPlaceholder: '结束日期',
valueFormat: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'YYYY-MM-DD HH:mm:ss',

View File

@@ -42,7 +42,8 @@ const searchConfig = reactive([
clearable: true, clearable: true,
placeholder: '请输入资金金额查询' placeholder: '请输入资金金额查询'
} }
}, { },
{
label: '剩余金额(元)', label: '剩余金额(元)',
prop: 'residualAmount', prop: 'residualAmount',
component: 'el-input', component: 'el-input',
@@ -50,7 +51,8 @@ const searchConfig = reactive([
clearable: true, clearable: true,
placeholder: '请输入剩余金额查询' placeholder: '请输入剩余金额查询'
} }
}, { },
{
label: '项目数量', label: '项目数量',
prop: 'projectNumber', prop: 'projectNumber',
component: 'el-input', component: 'el-input',

View File

@@ -143,7 +143,7 @@ const init = async () => {
form.value.setValues({state: '1', template: false}) form.value.setValues({state: '1', template: false})
const res = await getTemRoleOption() const res = await getTemRoleOption()
localData.tempRoleOpt = res.data localData.tempRoleOpt = res.data
const { data } = await getMenuOptRole() const { data } = await getMenuOptRole(0)
localData.menuData = data.menuOption localData.menuData = data.menuOption
// localData.checked = data.checked // localData.checked = data.checked
} }

View File

@@ -191,6 +191,14 @@ const init = async () => {
localData.roleOpt = roleRes.data localData.roleOpt = roleRes.data
const jobRes = await getJobOpt() const jobRes = await getJobOpt()
localData.jobOpt = jobRes.data localData.jobOpt = jobRes.data
// const reqList = [
// getDeptOpt(),
// getSubCompOpt(),
// getRolesOpt(),
// getJobOpt()
// ]
// const resAll = await Promise.all(reqList)
// console.log("🚀 ~ init ~ resAll:", resAll)
} }
const getInfo = async () => { const getInfo = async () => {

View File

@@ -114,6 +114,12 @@ const tableConfig = reactive({
label: '手机号码', label: '手机号码',
align: 'center', align: 'center',
}, },
{
prop: 'accountType',
label: '主子账号',
align: 'center',
currentRender: ({row, index}) => (<Tag dictType={'account_type'} value={row.accountType} />)
},
{ {
prop: 'state', prop: 'state',
label: '状态', label: '状态',

View File

@@ -57,7 +57,7 @@ import ProcessSetting from "./ProcessSetting.vue";
import Ellipsis from '@/views/workflow/process/common/Ellipsis.vue' import Ellipsis from '@/views/workflow/process/common/Ellipsis.vue'
import {getCurrentInstance} from '@vue/runtime-core'; import {getCurrentInstance} from '@vue/runtime-core';
import {useTagsView} from '@/stores/tagsview.js' import {useTagsView} from '@/stores/tagsview.js'
import {ElMessageBox,ElNotification} from "element-plus"; import {ElMessageBox, ElNotification} from "element-plus";
const tagsViewStore = useTagsView() const tagsViewStore = useTagsView()
@@ -87,7 +87,7 @@ const validOptions = ref([
{title: '审批流程', description: '', icon: '', status: ''}, {title: '审批流程', description: '', icon: '', status: ''},
// {title: '扩展设置', description: '', icon: '', status: ''} // {title: '扩展设置', description: '', icon: '', status: ''}
]) ])
onActivated(()=>{ onActivated(() => {
activeSelect.value = 'processSetting' activeSelect.value = 'processSetting'
init() init()
}) })
@@ -157,27 +157,31 @@ const loadInitFrom = () => {
type: "END", type: "END",
} }
], ],
processFromPerms: [{ processFromPerms: [
id: "projectName", {
title: "项目名称", id: "projectName",
required: true, title: "项目名称",
perm: "R" required: true,
}, { perm: "R"
id: "projectType", },
title: "项目类型", {
required: true, id: "projectType",
perm: "R" title: "项目类型",
}, { required: true,
id: "projectDesc", perm: "R"
title: "项目描述", },
required: true, {
perm: "R" id: "projectDesc",
}, { title: "项目描述",
id: "projectManager", required: true,
title: "项目经理", perm: "R"
required: true, },
perm: "R" {
}], id: "projectManager",
title: "项目经理",
required: true,
perm: "R"
}],
remark: "备注说明" remark: "备注说明"
} }
processStore.setDesign(design) processStore.setDesign(design)
@@ -332,7 +336,7 @@ const doPublish = () => {
ElNotification({ ElNotification({
title: '提示', title: '提示',
message: err, message: err,
type:'error' type: 'error'
}) })
}) })
}) })

View File

@@ -402,7 +402,8 @@ const insertConditionsNode = (node) => {
props: deepCopy(DefaultProps.CONDITION_PROPS), props: deepCopy(DefaultProps.CONDITION_PROPS),
name: "条件1", name: "条件1",
children: {} children: {}
}, { },
{
id: getRandomId(), id: getRandomId(),
parentId: node.id, parentId: node.id,
type: "CONDITION", type: "CONDITION",
@@ -437,7 +438,8 @@ const insertConcurrentsNode = (node) => {
props: deepCopy(DefaultProps.CONDITION_PROPS), props: deepCopy(DefaultProps.CONDITION_PROPS),
name: "分支1", name: "分支1",
children: {} children: {}
}, { },
{
id: getRandomId(), id: getRandomId(),
parentId: node.id, parentId: node.id,
type: "CONCURRENT", type: "CONCURRENT",

View File

@@ -5,7 +5,7 @@
<div class="picker"> <div class="picker">
<div class="candidate" v-loading="loading"> <div class="candidate" v-loading="loading">
<el-input v-model="filterText" @change="getList(1)" <el-input v-model="filterText" @change="getList(1)"
clearable placeholder="输入部门/昵称进行搜索"> clearable placeholder="输入昵称进行搜索">
<template #append> <template #append>
<el-button @click="getList(1)">搜索</el-button> <el-button @click="getList(1)">搜索</el-button>
</template> </template>
@@ -20,7 +20,7 @@
<template #default="{ node, data }"> <template #default="{ node, data }">
<div class="tree-node"> <div class="tree-node">
<div v-if="data.type === 0" style="display: flex;align-items: center;padding: 3px 0"> <div v-if="data.type === 0" style="display: flex;align-items: center;padding: 3px 0">
<el-icon> <el-icon :color="data.accountType==='0'?'#95d475':'#409eff'">
<UserFilled/> <UserFilled/>
</el-icon> </el-icon>
{{ node.label }}-{{ data.companyName }} {{ node.label }}-{{ data.companyName }}
@@ -192,6 +192,7 @@ const showUserPicker = () => {
radio.value = 0; radio.value = 0;
visible.value = true; visible.value = true;
expandedKeys.value = []; expandedKeys.value = [];
filterText.value=''
getList(); getList();
}; };
const handleChange = (item, data) => { const handleChange = (item, data) => {