WIP: 新增日程弹窗编写

This commit is contained in:
Xu Zhimeng 2023-05-17 20:10:10 +08:00
parent f816e78581
commit cf8017581b
6 changed files with 486 additions and 116 deletions

View File

@ -6,13 +6,14 @@
</a-config-provider> </a-config-provider>
</template> </template>
<script> <script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN' // import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import en_GB from 'ant-design-vue/lib/locale-provider/en_GB'
import enquireScreen from '@/utils/device' import enquireScreen from '@/utils/device'
export default { export default {
data () { data () {
return { return {
locale: zhCN, locale: en_GB,
} }
}, },
created () { created () {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,95 @@
<template>
<div class="popover-search">
<a-popover v-model="visible" trigger="click" placement="bottom" :overlayStyle="{ width: width + 'px' }">
<a-input ref="inputRef" v-model="keyword" @click.stop="">
<template slot="suffix">
<img class="popover-search-btn" src="@/assets/images/global/search-blue.png" @click.stop="onSearch" />
</template>
</a-input>
<template slot="content">
<template v-if="isLoading">
<a-spin :spinning="isLoading"></a-spin>
</template>
<template v-else>
<div
class="popover-search-item"
v-for="option in innerOptions"
:key="option.value"
@click="option.checked = !option.checked"
>
<a-checkbox v-model="option.checked"></a-checkbox>
<span class="popover-search-item-inner">{{ option.label }}</span>
</div>
</template>
</template>
</a-popover>
</div>
</template>
<script>
import { cloneDeep } from 'lodash'
export default {
props: {
remoteMethod: {
type: Function,
required: true
},
options: {
type: Array,
required: true
},
value: {
type: Array
}
},
data() {
return {
keyword: '',
visible: false,
width: 0,
isLoading: true,
innerOptions: []
}
},
methods: {
async onSearch() {
const style = window.getComputedStyle(this.$refs.inputRef.$el)
this.width = parseInt(style.width)
this.visible = true
if (this.remoteMethod && typeof this.remoteMethod == 'function') {
this.isLoading = true
await this.remoteMethod()
this.isLoading = false
}
}
},
watch: {
options: {
immediate: true,
handler() {
const options = cloneDeep(this.options)
options.forEach(item => (item.checked = false))
this.innerOptions = options
}
}
}
}
</script>
<style lang="less" scoped>
.popover-search {
&-btn {
cursor: pointer;
}
&-item {
height: 30px;
cursor: pointer;
padding: 4px 14px;
user-select: none;
&:hover {
background-color: #055565;
}
&-inner {
margin-left: 5px;
}
}
}
</style>

View File

@ -28,6 +28,7 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.ant-select { .ant-select {
width: 100%;
.ant-select-arrow-icon { .ant-select-arrow-icon {
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
} }

View File

@ -194,7 +194,7 @@ body {
&-content { &-content {
margin-top: 10px; margin-top: 10px;
height: 98px; height: 98px;
background-color: rgba(140, 255, 229, .1); background-color: rgba(140, 255, 229, 0.1);
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 4px !important; width: 4px !important;
} }
@ -216,7 +216,7 @@ body {
&-last-month-cell, &-last-month-cell,
&-next-month-btn-day { &-next-month-btn-day {
.ant-fullcalendar-value { .ant-fullcalendar-value {
color: rgba(173, 230, 238, .1) !important; color: rgba(173, 230, 238, 0.1) !important;
} }
} }
@ -439,13 +439,34 @@ body {
// 树形结构 // 树形结构
.ant-tree { .ant-tree {
&-checkbox-inner { &-checkbox-inner {
background-color: #03353f; background-color: #03353f !important;
border-color: #0a544e; border-color: #0a544e !important;
border-radius: 0; border-radius: 0;
} }
&-node-content-wrapper { &-node-content-wrapper {
background-color: transparent !important; background-color: transparent !important;
} }
&-treenode-disabled {
.ant-tree-checkbox-inner {
border-color: #0a544e !important;
}
.ant-tree-checkbox-disabled {
.ant-tree-checkbox-inner {
&::after {
border-color: rgba(255, 255, 255, .2) !important;
}
}
}
.ant-tree-title {
color: #fff !important;
}
}
&-node-selected {
.ant-tree-title {
color: #0cebc9;
}
}
} }
// 按钮 // 按钮
@ -613,7 +634,9 @@ body {
.ant-message { .ant-message {
&-notice { &-notice {
&-content { &-content {
border: 1px solid @formInputBorderColor;
background-color: @modalBg; background-color: @modalBg;
border-radius: 0;
} }
} }
} }
@ -656,7 +679,14 @@ body {
.ant-popover { .ant-popover {
&-inner { &-inner {
background-color: @modalBg; background-color: #03353f;
&-content {
padding: 0;
}
}
&-arrow {
border-left-color: #03353f !important;
border-top-color: #03353f !important;
} }
} }
@ -677,6 +707,9 @@ body {
border-radius: 0; border-radius: 0;
border-bottom-color: #224852; border-bottom-color: #224852;
} }
&-body {
overflow: auto;
}
} }
&-operation { &-operation {
.ant-btn { .ant-btn {
@ -692,6 +725,9 @@ body {
} }
} }
.ant-divider {
background-color: @formInputBorderColor;
}
#userLayout { #userLayout {
.container { .container {

View File

@ -48,7 +48,7 @@
<a-col :span="6"> <a-col :span="6">
<a-form-model class="search-form-form"> <a-form-model class="search-form-form">
<a-form-model-item label="Month" style="marign-bottom: 0"> <a-form-model-item label="Month" style="marign-bottom: 0">
<a-month-picker v-model="currentMonth"></a-month-picker> <a-month-picker v-model="currentMonth" :allow-clear="false"></a-month-picker>
</a-form-model-item> </a-form-model-item>
</a-form-model> </a-form-model>
</a-col> </a-col>
@ -89,46 +89,79 @@
<!-- 增加/编辑排班弹窗 --> <!-- 增加/编辑排班弹窗 -->
<custom-modal :title="isAdd ? 'Add' : 'Edit'" :width="845" v-model="visible" :okHandler="submit"> <custom-modal :title="isAdd ? 'Add' : 'Edit'" :width="845" v-model="visible" :okHandler="submit">
<a-transfer <div class="account-assign">
:data-source="[{ key: '1', title: '标题', description: '描述' }]" <a-transfer
:render="item => item.title" :data-source="stationList"
:operations="['Assign', 'Remove']" :target-keys="targetKeys"
:titles="['Particulate Station', 'Roster personnel']" :render="item => item.title"
:show-select-all="false" :operations="['Assign', 'Remove']"
> :titles="['Particulate Station', 'Roster personnel']"
<template slot="children" slot-scope="{ props: { direction, selectedKeys }, on: { itemSelect } }"> :show-select-all="false"
<a-tree @change="onChange"
v-if="direction === 'left'" >
blockNode <template slot="children" slot-scope="{ props: { direction, selectedKeys }, on: { itemSelect } }">
checkable <!-- 左侧穿梭框中的树 -->
checkStrictly <a-tree
defaultExpandAll v-if="direction === 'left'"
:checkedKeys="[...selectedKeys, ...targetKeys]" blockNode
:treeData="treeData" checkable
@check=" checkStrictly
(_, props) => { defaultExpandAll
onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect) :checkedKeys="[...selectedKeys, ...targetKeys]"
} :treeData="treeData"
" @check="
@select=" (_, props) => {
(_, props) => { onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect)
onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect) }
} "
" @select="
(_, props) => {
onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect)
}
"
/>
<!-- 右侧穿梭框中的树 -->
<a-tree
v-if="direction === 'right'"
blockNode
checkStrictly
defaultExpandAll
:treeData="accountTreeData"
:selectedKeys.sync="rightAccountChildSelectedKeys"
@select="
(_, props) => {
onSelectAccount(_, props, itemSelect)
}
"
/>
</template>
</a-transfer>
<!-- 穿梭框右上方搜索 -->
<div class="account-search">
<label>User Name</label>
<!-- <a-select
:options="accountList"
mode="multiple"
show-search
:filter-option="filterOption"
style="width: 190px"
v-model="selectedAccount"
> >
<a-icon slot="switcherIcon" type="down" /> <div slot="dropdownRender" slot-scope="menu">
</a-tree> <v-nodes :vnodes="menu" />
<a-tree <div class="account-add" @click="onAddToList()">
v-if="direction === 'right'" <a>Add</a>
blockNode </div>
checkable </div>
checkStrictly <a-select-option v-for="(account, index) of accountList" :key="index" :value="index">
defaultExpandAll {{ account.title }}
:checkedKeys="[...selectedKeys, ...targetKeys]" </a-select-option>
:treeData="treeData" </a-select> -->
/> <custom-popover-search :options="accountList" :remote-method="getAccountList"></custom-popover-search>
</template> </div>
</a-transfer> <!-- 穿梭框右上方搜索结束 -->
</div>
</custom-modal> </custom-modal>
<!-- 增加/编辑排班弹窗结束 --> <!-- 增加/编辑排班弹窗结束 -->
</div> </div>
@ -136,34 +169,97 @@
<script> <script>
import FormMixin from '@/mixins/FormMixin' import FormMixin from '@/mixins/FormMixin'
import moment from 'moment' import moment from 'moment'
import { cloneDeep } from 'lodash'
import { getAction } from '@/api/manage'
import CustomPopoverSearch from '@/components/CustomPopoverSearch'
export default { export default {
mixins: [FormMixin], mixins: [FormMixin],
components: {
VNodes: {
functional: true,
render: (_, ctx) => ctx.props.vnodes
},
CustomPopoverSearch
},
data() { data() {
return { return {
currentMonth: moment(), currentMonth: moment(),
form: {}, form: {},
visible: true, visible: true,
treeData: [ originalTreeData: [],
{ key: '0-0', title: '0-0' }, targetKeys: [],
{
key: '0-1', isGettingStationList: false,
title: '0-1', isGettingAccountList: false,
children: [ stationList: [],
{ key: '0-1-0', title: '0-1-0' }, accountList: [],
{ key: '0-1-1', title: '0-1-1' } selectedAccount: [], // User Name
] accountTreeData: [],
}, checkedAccount: '', // 穿
{ key: '0-2', title: '0-3' } rightAccountChildSelectedKeys: [] // 穿
],
selectedKeys: [],
targetKeys: []
} }
}, },
created() {
this.getStationList()
this.getAccountList()
},
methods: { methods: {
// //
async getList() {}, async getList() {},
async getStationList() {
try {
this.isGettingStationList = true
const { success, result, message } = await getAction('/gardsStations/findPage?pageIndex=1&pageSize=1000')
if (success) {
const records = result.records
const set = new Set(records.map(item => item.countryCode))
this.originalTreeData = Array.from(set).map((countryCode, index) => {
return {
title: countryCode,
key: index.toString(),
disabled: true,
children: records
.filter(item => item.countryCode == countryCode)
.map(item => ({ title: item.stationCode, key: item.stationId.toString(), children: [] }))
}
})
this.stationList = records.map(item => ({
title: item.stationCode,
key: item.stationId.toString()
}))
} else {
this.$message.error(message)
}
} catch (error) {
console.error(error)
} finally {
this.isGettingStationList = false
}
},
async getAccountList() {
try {
this.isGettingAccountList = true
const { success, result, message } = await getAction('/sys/user/list?pageIndex=1&pageSize=1000')
if (success) {
const records = result.records
this.accountList = records.map(item => ({
label: item.realname,
value: item.id
}))
} else {
this.$message.error(message)
}
} catch (error) {
console.error(error)
} finally {
this.isGettingAccountList = false
}
},
// //
onDel(item) { onDel(item) {
console.log('%c [ 删除 ]-51', 'font-size:13px; background:pink; color:#bf2c9f;', item) console.log('%c [ 删除 ]-51', 'font-size:13px; background:pink; color:#bf2c9f;', item)
@ -187,18 +283,118 @@ export default {
console.log('%c [ 新增 ]-88', 'font-size:13px; background:pink; color:#bf2c9f;') console.log('%c [ 新增 ]-88', 'font-size:13px; background:pink; color:#bf2c9f;')
}, },
/**
* 以下是对穿梭框的处理
*/
isChecked(selectedKeys, eventKey) { isChecked(selectedKeys, eventKey) {
return selectedKeys.indexOf(eventKey) !== -1 return selectedKeys.indexOf(eventKey) !== -1
}, },
onChecked(_, e, checkedKeys, itemSelect) { onChecked(_, e, checkedKeys, itemSelect) {
const { eventKey } = e.node const { eventKey } = e.node
itemSelect(eventKey, !this.isChecked(checkedKeys, eventKey)) itemSelect(eventKey, !this.isChecked(checkedKeys, eventKey))
},
// station
handleTreeData(data, targetKeys = [], level) {
data.forEach(item => {
if (level !== 0) {
item.disabled = targetKeys.includes(item.key)
}
if (item.children) {
this.handleTreeData(item.children, targetKeys)
}
})
return data
},
filterOption(input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
// 穿
onAddToList() {
this.accountTreeData = this.selectedAccount.map(id => {
const find = this.accountList.find(account => account.value == id)
return {
title: find.label,
key: id,
children: []
}
})
this.checkedAccount = ''
},
// 穿
onSelectAccount(_, e, itemSelect) {
const { eventKey, isLeaf, selected } = e.node
// selected selected false
if (isLeaf) {
//
if (selected) {
//
itemSelect(eventKey, false)
} else {
this.accountTreeData.forEach(account => {
// 穿
account.children.forEach(child => {
itemSelect(child.key, false)
})
})
itemSelect(eventKey, true) //
}
} else {
this.checkedAccount = selected ? '' : eventKey
}
},
// 穿
onChange(targetKeys, direction, moveKeys) {
if (direction == 'right') {
if (!this.checkedAccount) {
this.$message.warning('Please Select A Person To Assign')
return
}
const findAccount = this.accountTreeData.find(account => account.key == this.checkedAccount)
if (findAccount) {
const children = moveKeys.map(key => {
const findStation = this.stationList.find(station => station.key == key)
return {
isLeaf: true,
...cloneDeep(findStation)
}
})
findAccount.children.push(...children)
}
} else {
const moveKey = moveKeys[0]
let parentIndex = -1,
childIndex = -1
for (const pIndex in this.accountTreeData) {
// Station
const account = this.accountTreeData[pIndex]
const cIndex = account.children.findIndex(child => child.key == moveKey)
if (-1 !== cIndex) {
parentIndex = pIndex
childIndex = cIndex
break
}
}
this.accountTreeData[parentIndex].children.splice(childIndex, 1)
}
this.targetKeys = targetKeys
} }
}, },
watch: { watch: {
currentMonth() { currentMonth() {
this.getList() this.getList()
} }
},
computed: {
treeData() {
return this.handleTreeData(cloneDeep(this.originalTreeData), this.targetKeys, 0)
}
} }
} }
</script> </script>
@ -378,71 +574,112 @@ export default {
} }
} }
::v-deep { .account-assign {
position: relative;
width: 672px;
margin: 0 auto;
.ant-transfer { .ant-transfer {
margin-bottom: 10px; margin-bottom: 10px;
::v-deep {
.ant-transfer-list {
width: 282px;
height: 411px;
&-header {
height: 37px;
&-selected {
span:first-child {
display: none;
}
}
&-title {
left: 16px;
}
}
&-content {
&-item {
&:hover {
background-color: transparent;
}
}
}
&-list { &:last-child {
width: 282px; height: 364px;
height: 374px; position: relative;
&-header { top: 47px;
height: 37px; }
&-selected { }
span:first-child {
.ant-transfer-operation {
.ant-btn {
width: 92px;
height: 26px;
padding: 0;
.anticon {
display: none; display: none;
} }
} span {
&-title { margin-left: 0;
left: 16px;
}
}
&-content {
&-item {
&:hover {
background-color: transparent;
} }
} &:first-child {
} margin-bottom: 52px;
} &::after {
display: inline-block;
&-operation { margin-left: 13px;
.ant-btn { content: '';
width: 92px; width: 18px;
height: 26px; height: 10px;
padding: 0; background: url(~@/assets/images/system/transfer-right.png) no-repeat;
.anticon { background-size: contain;
display: none; }
}
span {
margin-left: 0;
}
&:first-child {
margin-bottom: 52px;
&::after {
display: inline-block;
margin-left: 13px;
content: '';
width: 18px;
height: 10px;
background: url(~@/assets/images/system/transfer-right.png) no-repeat;
background-size: contain;
} }
} &:nth-child(2) {
&:nth-child(2) { &::before {
&::before { display: inline-block;
display: inline-block; margin-right: 6px;
margin-right: 6px; content: '';
content: ''; width: 18px;
width: 18px; height: 10px;
height: 10px; background: url(~@/assets/images/system/transfer-left.png) no-repeat;
background: url(~@/assets/images/system/transfer-left.png) no-repeat; background-size: contain;
background-size: contain; position: static;
position: static; opacity: initial;
opacity: initial; }
} }
} }
} }
} }
} }
.account-search {
position: absolute;
top: 0;
right: 0;
width: 282px;
display: flex;
align-items: center;
label {
color: #5b9cba;
font-size: 16px;
flex-shrink: 0;
margin-right: 10px;
}
}
}
.ant-select-dropdown-content::before {
top: 2px;
}
.account-add {
padding: 0;
padding-top: 10px;
text-align: center;
cursor: pointer;
background: #03353f;
a {
display: inline-block;
width: 100%;
padding: 5px;
border-top: 1px solid #0da397;
color: #0cebc9;
}
} }
</style> </style>