This commit is contained in:
磷叶 2025-01-13 17:21:07 +08:00
parent 92d22a29b6
commit eb113f4b7d
7 changed files with 882 additions and 22 deletions

View File

@ -9,6 +9,15 @@ export function listPrice(query) {
})
}
// 查询查询条件
export function listPriceSearchCondition(query) {
return request({
url: '/yh/price/searchCondition',
method: 'get',
params: query
})
}
// 查询单价列表ByIds
export function listPriceByIds(priceIds) {
return request({

View File

@ -0,0 +1,504 @@
<template>
<el-drawer
:visible.sync="drawerVisible"
:size="drawerSize"
title="选择工序"
:destroy-on-close="true"
:with-header="false"
append-to-body
@open="onOpen"
:before-close="beforeClose"
>
<el-row :gutter="8" class="drawer-box">
<el-col :span="8">
<div class="condition-box">
<div>
<div class="condition-title">类别
<el-input v-model="queryParams.category" placeholder="请输入类别" clearable @input="getList" size="mini" class="input"/>
</div>
<el-radio-group v-model="queryParams.category" @change="getList">
<el-radio v-for="item of categoryList" :label="item.label">{{item.label}} ({{item.count}})</el-radio>
</el-radio-group>
</div>
<div>
<div class="condition-title">尺寸
<el-input v-model="queryParams.size" placeholder="请输入尺寸" clearable @input="getList" size="mini" class="input"/>
</div>
<el-radio-group v-model="queryParams.size" @change="getList">
<el-radio v-for="item of sizeList" :label="item.label">{{item.label}} ({{item.count}})</el-radio>
</el-radio-group>
</div>
<div>
<div class="condition-title">表面处理
<el-input v-model="queryParams.surface" placeholder="请输入表面处理" clearable @input="getList" size="mini" class="input"/>
</div>
<el-radio-group v-model="queryParams.surface" @change="getList">
<el-radio v-for="item of surfaceList" :label="item.label">{{item.label}} ({{item.count}})</el-radio>
</el-radio-group>
</div>
<div>
<div class="condition-title">图案
<el-input v-model="queryParams.pattern" placeholder="请输入图案" clearable @input="getList" size="mini" class="input"/>
</div>
<el-radio-group v-model="queryParams.pattern" @change="getList">
<el-radio v-for="item of patternList" :label="item.label">{{item.label}} ({{item.count}})</el-radio>
</el-radio-group>
</div>
</div>
</el-col>
<el-col :span="16" class="user-drawer">
<!-- 搜索区域 -->
<div class="search-area">
<div class="search-wrapper">
<el-input
v-model="queryParams.priceName"
placeholder="搜索工序名称"
clearable
prefix-icon="el-icon-search"
@input="onInputPriceName"
/>
<el-button icon="el-icon-refresh" @click="resetParams" type="text">重置筛选</el-button>
<el-button
type="text"
class="select-all-btn"
@click="toggleSelectAll"
>
{{ isAllSelected ? '取消全选' : '全选' }}
</el-button>
</div>
</div>
<div class="price-example">
图例
<el-tag type="success" size="mini">类别</el-tag>
<el-tag type="warning" size="mini">大小</el-tag>
<el-tag type="primary" size="mini">表面处理</el-tag>
<el-tag type="danger" size="mini">图案</el-tag>
</div>
<!-- 工序列表区域 -->
<div
class="user-list"
v-infinite-scroll="loadMore"
:infinite-scroll-immediate="false"
infinite-scroll-distance="10"
>
<div
v-for="item in dataList"
:key="item.priceId"
class="user-item"
:class="{ 'is-selected': selectedIds.includes(item.priceId) }"
@click="toggleSelection(item)"
>
<div class="price-info">
<div class="price-main">
<span class="price-name">{{ item.name }}</span>
<span class="price-code" v-if="item.code">{{ item.code }}</span>
</div>
<div class="price-detail">
<span class="dept-name">{{ item.deptName }}</span>
<span class="price-attrs">
<template v-if="item.category">
<el-tag size="mini" type="success">{{ item.category }}</el-tag>
</template>
<template v-if="item.size">
<el-tag size="mini" type="warning">{{ item.size }}</el-tag>
</template>
<template v-if="item.surface">
<el-tag size="mini" type="primary">{{ item.surface }}</el-tag>
</template>
<template v-if="item.pattern">
<el-tag size="mini" type="danger">{{ item.pattern }}</el-tag>
</template>
</span>
<span class="price-amount">
<span class="amount">{{ item.price }}</span>
<span class="unit" v-if="item.unit">/{{ item.unit }}</span>
</span>
</div>
</div>
<div class="user-selected">
<i
v-show="selectedIds.includes(item.priceId)"
class="el-icon-check"
></i>
</div>
</div>
<div v-if="loading" class="loading-more">
<i class="el-icon-loading"></i>
加载中...
</div>
<div v-if="isFinished" class="loading-more">
没有更多数据了
</div>
</div>
<!-- 底部操作区 -->
<div class="drawer-footer">
<el-button @click="drawerVisible = false">取消</el-button>
<el-button type="primary" @click="confirmSelection">确定</el-button>
</div>
</el-col>
</el-row>
</el-drawer>
</template>
<script>
import { listPrice, listPriceByIds, listPriceSearchCondition } from '@/api/yh/price'
import { isEmpty } from '@/utils/index'
export default {
name: 'PriceNewDrawer',
props: {
show: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: true
},
defaultIds: {
type: Array,
default: () => []
},
query: {
type: Object,
default: () => {}
},
listApi: {
type: Function,
default: listPrice
},
loadApi: {
type: Function,
default: listPriceByIds
}
},
data() {
return {
dataList: [],
loading: false,
selectedIds: [],
queryParams: {
pageNum: 1,
pageSize: 10,
priceName: null,
category: null,
size: null,
surface: null,
pattern: null,
},
total: null,
isAllSelected: false,
//
condition: {
categoryList: [],
sizeList: [],
surfaceList: [],
patternList: [],
}
}
},
computed: {
drawerVisible: {
get() {
return this.show
},
set(val) {
this.$emit('update:show', val)
}
},
drawerSize() {
return window.innerWidth < 768 ? '100%' : '800px'
},
isFinished() {
return this.total != null && this.dataList.length >= this.total;
},
categoryList() {
return this.condition != null && this.condition.categoryList != null ? this.condition.categoryList.filter(item => !isEmpty(item.label)) : [];
},
sizeList() {
return this.condition != null && this.condition.sizeList != null ? this.condition.sizeList.filter(item => !isEmpty(item.label)) : [];
},
surfaceList() {
return this.condition != null && this.condition.surfaceList != null ? this.condition.surfaceList.filter(item => !isEmpty(item.label)) : [];
},
patternList() {
return this.condition != null && this.condition.patternList != null ? this.condition.patternList.filter(item => !isEmpty(item.label)) : [];
}
},
methods: {
isEmpty,
resetParams() {
this.queryParams.category = null;
this.queryParams.size = null;
this.queryParams.surface = null,
this.queryParams.pattern = null;
this.queryParams.priceName = null;
this.getList();
},
onInputPriceName() {
this.getList();
},
onOpen() {
this.selectedIds = [...this.defaultIds];
this.isAllSelected = false;
this.resetParams();
},
getList() {
this.queryParams.pageNum = 0;
this.queryParams = {
...this.queryParams,
...this.query
}
this.dataList = [];
this.total = null;
this.isAllSelected = false;
this.loadMore();
this.selectCondition();
},
//
async selectCondition() {
let res = await listPriceSearchCondition(this.queryParams);
if (res.code == 200) {
this.condition = res.data;
}
},
async loadMore() {
if (this.loading) {
return;
}
if (this.isFinished) {
return;
}
this.loading = true
this.queryParams.pageNum++
try {
// API
const res = await this.listApi(this.queryParams);
this.dataList.push(...res.rows)
this.total = res.total;
if (this.isAllSelected) {
const newIds = res.rows
.map(user => user.priceId)
.filter(id => !this.selectedIds.includes(id))
this.selectedIds.push(...newIds)
}
} finally {
this.loading = false
}
},
toggleSelection(user) {
if (!this.multiple) {
this.selectedIds = [user.priceId]
this.confirmSelection();
} else {
const index = this.selectedIds.indexOf(user.priceId)
if (index === -1) {
this.selectedIds.push(user.priceId)
} else {
this.selectedIds.splice(index, 1)
}
}
},
async confirmSelection() {
if (this.selectedIds == null || this.selectedIds.length == 0) {
this.$message.warning('请选择工序');
return;
}
let res = await this.loadApi(this.selectedIds);
if (res.code == 200) {
this.$emit('confirm', res.data)
this.drawerVisible = false
}
},
toggleSelectAll() {
if (this.isAllSelected) {
this.selectedIds = []
this.isAllSelected = false
} else {
this.selectedIds = this.dataList.map(user => user.priceId)
this.isAllSelected = true
}
},
beforeClose(done) {
if (this.selectedIds != null && this.selectedIds.length > 0) {
this.$confirm('是否选中当前数据?').then(() => {
done();
this.confirmSelection();
}).catch(() => {
done();
});
} else {
done();
}
}
},
}
</script>
<style lang="scss" scoped>
.drawer-box {
height: 100%;
display: block;
position: relative;
.condition-box {
height: 100%;
overflow: auto;
padding: 1em;
.condition-title {
margin: 8px 0;
display: flex;
.input {
flex: 1;
margin-left: 8px;
}
}
}
}
.user-drawer {
height: 100%;
display: flex;
flex-direction: column;
.price-example {
padding: 16px 20px;
border-bottom: 1px solid #eee;
color: #909399;
font-size: 13px;
}
.search-area {
margin-top: 16px;
padding: 0 20px 16px;
border-bottom: 1px solid #eee;
.search-wrapper {
display: flex;
align-items: center;
gap: 12px;
.el-input {
flex: 1;
}
.select-all-btn {
white-space: nowrap;
padding: 0 15px;
}
}
}
.user-list {
flex: 1;
overflow-y: auto;
padding: 12px 20px;
.user-item {
display: flex;
align-items: center;
padding: 10px 12px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #f5f7fa;
}
&.is-selected {
background-color: #ecf5ff;
.price-name {
color: #409eff !important;
}
}
.price-info {
flex: 1;
min-width: 0;
.price-main {
display: flex;
align-items: center;
margin-bottom: 4px;
.price-name {
font-size: 15px;
font-weight: 500;
margin-right: 8px;
color: #303133;
}
.price-code {
font-size: 13px;
color: #909399;
background: #f5f7fa;
padding: 0 6px;
border-radius: 3px;
}
}
.price-detail {
display: flex;
align-items: center;
font-size: 13px;
color: #606266;
gap: 8px;
.dept-name {
color: #606266;
}
.price-attrs {
display: flex;
gap: 4px;
flex-wrap: wrap;
.el-tag {
height: 20px;
line-height: 18px;
padding: 0 4px;
}
}
.price-amount {
margin-left: auto;
white-space: nowrap;
.amount {
color: #f56c6c;
font-weight: 500;
}
.unit {
color: #909399;
margin-left: 2px;
}
}
}
}
.user-selected {
width: 24px;
color: #409eff;
}
}
}
.loading-more {
text-align: center;
color: #909399;
padding: 12px 0;
}
.drawer-footer {
padding: 16px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
}
}
</style>

View File

@ -0,0 +1,338 @@
<template>
<el-drawer
:visible.sync="drawerVisible"
:size="drawerSize"
title="选择用户"
:destroy-on-close="true"
:with-header="false"
@open="onOpen"
append-to-body
:before-close="beforeClose"
>
<div class="user-drawer">
<!-- 搜索区域 -->
<div class="search-area">
<div class="search-wrapper">
<el-input
v-model="queryParams.keyword"
placeholder="搜索用户名称/工号"
clearable
prefix-icon="el-icon-search"
@input="getList"
/>
<el-button
type="text"
class="select-all-btn"
@click="toggleSelectAll"
>
{{ isAllSelected ? '取消全选' : '全选' }}
</el-button>
</div>
</div>
<!-- 用户列表区域 -->
<div
class="user-list"
v-infinite-scroll="loadMore"
:infinite-scroll-immediate="false"
infinite-scroll-distance="10"
>
<div
v-for="user in userList"
:key="user.userId"
class="user-item"
:class="{ 'is-selected': selectedIds.includes(user.userId) }"
@click="toggleSelection(user)"
>
<div class="user-avatar">
<el-avatar :size="40">{{ user.nickName.charAt(0) }}</el-avatar>
</div>
<div class="user-info">
<div class="user-primary">
<span class="user-name">{{ user.nickName }}</span>
<span class="user-no">{{ user.userNo }}</span>
</div>
<div class="user-department">{{ user.deptName }}</div>
</div>
<div class="user-selected">
<i
v-show="selectedIds.includes(user.userId)"
class="el-icon-check"
></i>
</div>
</div>
<div v-if="loading" class="loading-more">
<i class="el-icon-loading"></i>
加载中...
</div>
<div v-if="isFinished" class="loading-more">
没有更多数据了
</div>
</div>
<!-- 底部操作区 -->
<div class="drawer-footer">
<el-button @click="drawerVisible = false">取消</el-button>
<el-button type="primary" @click="confirmSelection">确定</el-button>
</div>
</div>
</el-drawer>
</template>
<script>
import { listUser, listUserByIds } from '@/api/system/user'
export default {
name: 'UserNewDrawer',
props: {
show: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: true
},
defaultIds: {
type: Array,
default: () => []
},
query: {
type: Object,
default: () => {}
},
listApi: {
type: Function,
default: listUser
},
loadApi: {
type: Function,
default: listUserByIds
}
},
data() {
return {
userList: [],
loading: false,
selectedIds: [],
queryParams: {
pageNum: 1,
pageSize: 10,
keyword: null,
},
total: null,
isAllSelected: false,
}
},
computed: {
drawerVisible: {
get() {
return this.show
},
set(val) {
this.$emit('update:show', val)
}
},
drawerSize() {
return window.innerWidth < 768 ? '100%' : '600px'
},
isFinished() {
return this.total != null && this.userList.length >= this.total;
}
},
methods: {
onOpen() {
this.selectedIds = [...this.defaultIds];
this.isAllSelected = false;
this.getList();
},
getList() {
this.queryParams.pageNum = 0;
this.queryParams = {
...this.queryParams,
...this.query
}
this.userList = [];
this.total = null;
this.isAllSelected = false;
this.loadMore();
},
async loadMore() {
if (this.loading) {
return;
}
if (this.isFinished) {
return;
}
this.loading = true
this.queryParams.pageNum ++
try {
// API
const res = await this.listApi(this.queryParams);
if (res.code === 200) {
this.userList.push(...res.rows)
this.total = res.total;
if (this.isAllSelected) {
const newIds = res.rows
.map(user => user.userId)
.filter(id => !this.selectedIds.includes(id))
this.selectedIds.push(...newIds)
}
}
} finally {
this.loading = false
}
},
toggleSelection(user) {
if (!this.multiple) {
this.selectedIds = [user.userId]
this.confirmSelection();
} else {
const index = this.selectedIds.indexOf(user.userId)
if (index === -1) {
this.selectedIds.push(user.userId)
} else {
this.selectedIds.splice(index, 1)
}
}
},
async confirmSelection() {
if (this.selectedIds == null || this.selectedIds.length == 0) {
this.$message.warning('请选择用户');
return;
}
let res = await this.loadApi(this.selectedIds);
if (res.code == 200) {
this.$emit('confirm', res.data)
this.drawerVisible = false
}
},
toggleSelectAll() {
if (this.isAllSelected) {
this.selectedIds = []
this.isAllSelected = false
} else {
this.selectedIds = this.userList.map(user => user.userId)
this.isAllSelected = true
}
},
beforeClose(done) {
if (this.selectedIds != null && this.selectedIds.length > 0) {
this.$confirm('是否选中当前数据?').then(() => {
done();
this.confirmSelection();
}).catch(() => {
done();
});
} else {
done();
}
}
},
}
</script>
<style lang="scss" scoped>
.user-drawer {
height: 100%;
display: flex;
flex-direction: column;
.search-area {
margin-top: 16px;
padding: 0 20px 16px;
border-bottom: 1px solid #eee;
.search-wrapper {
display: flex;
align-items: center;
gap: 12px;
.el-input {
flex: 1;
}
.select-all-btn {
white-space: nowrap;
padding: 0 15px;
}
}
}
.user-list {
flex: 1;
overflow-y: auto;
padding: 12px 20px;
.user-item {
display: flex;
align-items: center;
padding: 12px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: #f5f7fa;
}
&.is-selected {
background-color: #ecf5ff;
.user-name {
color: #409eff;
font-weight: bold;
}
}
.user-avatar {
margin-right: 12px;
}
.user-info {
flex: 1;
.user-primary {
display: flex;
align-items: center;
margin-bottom: 4px;
.user-name {
font-size: 16px;
font-weight: 500;
margin-right: 8px;
}
.user-no {
color: #909399;
font-size: 13px;
}
}
.user-department {
color: #606266;
font-size: 13px;
}
}
.user-selected {
width: 24px;
color: #409eff;
}
}
}
.loading-more {
text-align: center;
color: #909399;
padding: 12px 0;
}
.drawer-footer {
padding: 16px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
}
}
</style>

View File

@ -107,11 +107,18 @@
</div>
<el-empty v-else description="暂无工序,请添加" />
<price-drawer
<!-- <price-drawer
multiple
:show.sync="showPriceDialog"
:query="priceQuery"
@select="onSelectPrices"
/> -->
<price-new-drawer
multiple
:show.sync="showPriceDialog"
:query="priceQuery"
@confirm="onSelectPrices"
/>
<user-product-batch-dialog
@ -146,7 +153,7 @@ import ReportProductRowEdit from "@/views/yh/report/edit/components/ReportProduc
import PriceDrawer from "@/components/Business/Price/PriceDrawer.vue";
import {calcTotalAmount} from "@/views/yh/report/utils";
import UserProdPreview from './UserProdPreview.vue'
import PriceNewDrawer from "@/components/Business/Price/PriceNewDrawer.vue";
export default {
name: "ReportProductEdit",
dicts: ['price_type'],
@ -157,7 +164,7 @@ export default {
Dict,
ReportProductUserListEdit,
ReportProductOrderListEdit,
UserProductBatchDialog, FormCol, PriceInput, PriceDialog, UserInput, UserProdPreview},
UserProductBatchDialog, FormCol, PriceInput, PriceDialog, UserInput, UserProdPreview, PriceNewDrawer},
props: {
value: {
type: Array,

View File

@ -8,8 +8,8 @@
<div class="edit-title">工序信息</div>
<el-form :model="row" :rules="rules" ref="form" label-width="5em">
<el-row>
<form-col :span="span * 2" label="工序" prop="priceId">
<price-input v-model="row.priceId" :query="priceQuery" open-type="drawer" @change="onChangePrice" placeholder="点击选择工序" title="选择工序"/>
<form-col :span="span" label="工序" prop="priceId">
{{row.priceName | dv}}
</form-col>
<form-col :span="span" label="代码">
{{row.priceCode | dv}}
@ -19,10 +19,10 @@
<el-radio v-for="(item, index) in dict.type.price_type" :key="index" :label="item.value">{{item.label}}</el-radio>
</el-radio-group>
</form-col>
<form-col :span="span" label="单价">
<form-col :span="span / 2" label="单价">
{{row.pricePrice | dv}}
</form-col>
<form-col :span="span" label="总价">
<form-col :span="span / 2 " label="总价">
{{ row.totalAmount | dv }}
</form-col>
<form-col :span="span" label="良品" prop="num">
@ -52,6 +52,7 @@
:rules="rules.orderProdList"
:report-prod="row"
/>
</el-form>
</div>
</div>

View File

@ -4,7 +4,7 @@
<el-table-column label="员工" align="center">
<template slot-scope="d">
<form-col table label-width="0" :prop="`userProdList[${d.$index}].userId`" :rules="rules.userId">
<user-input v-model="d.row.userId" open-type="drawer" :list-api="listUserWithShift" :query="userQueryParams"/>
{{d.row.userName}}
</form-col>
</template>
</el-table-column>
@ -42,12 +42,11 @@
</el-table-column>
</el-table>
<!--用户选择-->
<user-drawer
multiple
<user-new-drawer
:show.sync="showUserDialog"
:multiple="true"
@confirm="onSelectUsers"
:list-api="listUserWithShift"
@select="onSelectUsers"
:query="userQueryParams"
/>
</div>
@ -62,10 +61,11 @@ import UserInput from "@/components/Business/User/UserInput.vue";
import {calcMulDecimal} from "@/utils/money";
import UserDrawer from "@/components/Business/User/UserDrawer.vue";
import {listUserWithShift} from "@/api/system/user";
import UserNewDrawer from "@/components/Business/User/UserNewDrawer.vue";
export default {
name: "ReportProductUserListEdit",
components: {UserDrawer, UserInput, UserDialog, PriceDialog, FormCol},
components: {UserDrawer, UserInput, UserDialog, PriceDialog, FormCol, UserNewDrawer},
props: {
value: {
type: Array,

View File

@ -32,7 +32,8 @@
</template>
<template slot-scope="d">
<form-col table label-width="0">
<user-input v-model="d.row.userId" :list-api="listUserWithShift" :query="userQueryParams" open-type="drawer" />
<!-- <user-input v-model="d.row.userId" :list-api="listUserWithShift" :query="userQueryParams" open-type="drawer" /> -->
{{d.row.userName | dv}}
</form-col>
</template>
</el-table-column>
@ -47,10 +48,10 @@
</template>
<template slot-scope="d">
<form-col table label-width="0" v-if="form.mode === IncomeMode.COUNT" :prop="`list[${d.$index}].num`" :rules="rules.list.num">
<el-input-number v-model="d.row.num" type="number" placeholder="请输入员工产量" :min="1" controls-position="right" style="width: 100%"/>
<el-input-number v-model="d.row.num" type="number" placeholder="请输入员工产量" :min="1" style="width: 100%"/>
</form-col>
<form-col table label-width="0" v-if="form.mode === IncomeMode.SCORE" :prop="`list[${d.$index}].score`" :rules="rules.list.score">
<el-input-number v-model="d.row.score" type="number" placeholder="请输入员工分数" :min="0" controls-position="right" style="width: 100%"/>
<el-input-number v-model="d.row.score" type="number" placeholder="请输入员工分数" :min="0" style="width: 100%"/>
</form-col>
</template>
</el-table-column>
@ -70,12 +71,12 @@
<el-button type="primary" @click="onSubmit()" icon="el-icon-check"> </el-button>
</el-row>
<user-drawer
<user-new-drawer
:show.sync="showUserDialog"
multiple
:query="userQueryParams"
:multiple="true"
@confirm="onSelectUsers"
:list-api="listUserWithShift"
@select="onSelectUsers"
:query="userQueryParams"
/>
</el-drawer>
@ -85,12 +86,12 @@
import FormCol from "@/components/FormCol/index.vue";
import UserInput from "@/components/Business/User/UserInput.vue";
import {IncomeMode} from "@/utils/constants";
import UserDrawer from "@/components/Business/User/UserDrawer.vue";
import UserNewDrawer from "@/components/Business/User/UserNewDrawer.vue";
import {listUserWithShift} from "@/api/system/user";
export default {
name: "UserProductBatchDialog",
components: {UserDrawer, UserInput, FormCol},
components: {UserNewDrawer, UserInput, FormCol},
props: {
show: {
type: Boolean,