123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- <template>
- <view class="container">
- <view class="form-section" @click="openPicker">
- <view class="section-title">选择客户</view>
- <u-input
- v-model="formData.customerName"
- type="select"
- placeholder="请选择客户"
- border="bottom"
- readonly
- ></u-input>
- </view>
- <u-popup :show="showCustomerPicker" mode="bottom" @close="closePicker">
- <!-- 顶部操作栏(渐变背景) -->
- <view class="picker-header">
- <text class="header-btn" @click="closePicker">取消</text>
- <text class="header-title">选择客户</text>
- <text class="header-btn confirm" @click="confirmSelection">确定</text>
- </view>
- <!-- 搜索框(带防抖) -->
- <view class="search-box">
- <u-search
- v-model="searchKeyword"
- placeholder="输入客户姓名搜索"
- shape="round"
- clearable
- :showAction="true" actionText="搜索"
- @search="handleSearch"
- @custom="handleSearch"
- @clear="resetSearch"
- ></u-search>
- </view>
- <!-- 客户列表 -->
- <scroll-view
- scroll-y
- class="customer-list"
- :style="{height: listHeight + 'px'}"
- @scrolltolower="loadMore"
- >
- <view
- v-for="(item, index) in dataList"
- :key="index"
- class="customer-card"
- :class="{active: selectedIndex === index}"
- @click="selectItem(index)"
- >
- <text class="customer-name">{{ item.cname }}</text>
- <u-icon
- v-if="selectedIndex === index"
- name="checkmark-circle-fill"
- color="#2979ff"
- size="36"
- ></u-icon>
- </view>
- <!-- 加载状态 -->
- <u-loadmore
- :status="loading ? 'loading' : (isLastPage ? 'nomore' : 'loadmore')"
- marginTop="20"
- marginBottom="20"
- />
- </scroll-view>
- </u-popup>
- <scroll-view scroll-y class="goods-list">
- <view v-for="(item, index) in goodsList" :key="index" class="goods-item">
- <view class="goods-info">
- <text class="goods-name">{{ item.productName }}</text>
- <text class="goods-price">¥{{ item.productPrice }}</text>
- </view>
- <view class="quantity-section">
- <u-number-box
- v-model="item.productQuantity"
- :min="0"
- integer
- class="quantity-box"
- ></u-number-box>
- <view class="amount-box">
- <text class="amount-symbol">¥</text>
- <text class="amount-number">{{ (item.productPrice * item.productQuantity).toFixed(2) }}</text>
- </view>
- </view>
- </view>
- </scroll-view>
- <view class="total-section">
- <text class="total-label">总金额:</text>
- <view class="total-amount-box">
- <text class="total-symbol">¥</text>
- <text class="total-number">{{ totalAmount.toFixed(2) }}</text>
- </view>
- </view>
- <view class="form-section">
- <view class="section-title">备注信息</view>
- <u-textarea
- v-model="formData.remark"
- placeholder="请输入备注(最多200字)"
- :maxlength="200"
- auto-height
- confirm-type="done"
- :count="true"
- :height="180"
- class="remark-textarea"
- placeholder-class="placeholder-style"
- ></u-textarea>
- </view>
- <view class="form-section">
- <view class="section-title">上传图片(最多3张)</view>
- <u-upload
- :fileList="fileList"
- @afterRead="afterRead"
- @delete="deletePic"
- multiple
- maxCount="3"
- :previewFullImage="true"
- uploadIcon="plus"
- uploadIconColor="#999"
- deletable
- :previewImage="true"
- class="upload-area"
- >
- <template #default>
- <view class="upload-tip">点击或拖拽上传图片</view>
- </template>
- </u-upload>
- </view>
- <view class="submit-section" v-if="!this.formData.status || this.formData.status !== 2">
- <button
- class="submit-btn"
- :disabled="totalAmount <= 0 || !formData.customerId"
- @click="clickSubmit"
- >
- {{this.formData.id ? '修改回收单' : '提交回收单'}}
- </button>
- </view>
- </view>
- </template>
- <script>
- import {
- submitRecycling,
- updateRecycling,
- getDetail,
- getCustomerPageList,
- greenRecyclingGoods
- } from '@/api/views/recycling/index.js'
- import http from '@/http/api.js'
- import {
- clientId,
- clientSecret
- } from '@/common/setting'
- export default {
- onLoad(options) {
- this.formData.id = options.id
- this.getGoodsList()
- console.log(options.id);
- },
- data() {
- return {
- showCustomerPicker: false,
- searchKeyword: '',
- dataList: [], // 当前页数据
- currentPage: 1, // 当前页码
- loading: false, // 加载状态
- isLastPage: false, // 是否最后一页
- selectedIndex: -1, // 选中项索引
- listHeight: 500, // 滚动区域高度,
- goodsList: [],
- fileList: [],
- formData: {
- id: null,
- quantity: null,
- amount: null,
- customerId: null,
- customerName: null,
- remark: '',
- imgs: [],
- greenRecyclingItemList: []
- },
- }
- },
- computed: {
- totalAmount() {
- return this.goodsList.reduce((total, item) => {
- return total + (item.productPrice * item.productQuantity)
- }, 0)
- }
- },
- methods: {
- selectItem(index) {
- this.selectedIndex = index;
- },
- async fetchData(reset = false) {
- if (this.loading) return;
- this.loading = true;
- try {
- const params = {
- current: reset ? 1 : this.currentPage,
- size: 10,
- cname: this.searchKeyword,
- corpType: 'KH'
- };
- getCustomerPageList(params).then(res => {
- console.info('获取数据成功:', res)
- if (reset) {
- this.dataList = res.data.records;
- this.currentPage = 1;
- } else {
- this.dataList = [...this.dataList, ...res.data.records];
- }
- this.isLastPage = this.currentPage * 10 >= res.data.total;
- })
- } catch (error) {
- console.error('获取数据失败:', error);
- } finally {
- this.loading = false;
- }
- },
- // 搜索处理(带防抖)
- handleSearch() {
- clearTimeout(this.timer);
- this.timer = setTimeout(() => {
- this.fetchData(true);
- }, 500);
- },
- // 加载更多
- loadMore() {
- if (!this.isLastPage && !this.loading) {
- this.currentPage++;
- this.fetchData();
- }
- },
- calcListHeight() {
- uni.getSystemInfo({
- success: (res) => {
- // 顶部操作栏88 + 搜索框60 + 底部安全区30
- this.listHeight = res.windowHeight - 88 - 60 - 30;
- }
- });
- },
- // 重置搜索
- resetSearch() {
- this.searchKeyword = '';
- },
- openPicker() {
- this.showCustomerPicker = true;
- this.calcListHeight();
- this.fetchData(true)
- },
- confirmSelection() {
- if (this.selectedIndex >= 0) {
- const selected = this.dataList[this.selectedIndex];
- console.info('selected----', selected)
- this.formData.customerId = selected.id
- this.formData.customerName = selected.cname
- this.$emit('selected', selected);
- }
- this.closePicker();
- },
- closePicker() {
- this.showCustomerPicker = false;
- },
- async afterRead(event) {
- // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
- let lists = [].concat(event.file)
- let fileListLen = this[`fileList${event.name}`].length
- lists.map((item) => {
- this[`fileList${event.name}`].push({
- ...item,
- })
- })
- for (let i = 0; i < lists.length; i++) {
- const result = await this.uploadFilePromise(lists[i].url)
- let item = this[`fileList${event.name}`][fileListLen]
- this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
- sort: this.fileList.length,
- fileName: JSON.parse(result).data.originalName,
- url: JSON.parse(result).data.link
- }))
- fileListLen++
- }
- },
- deletePic(event) {
- this.fileList.splice(event.index, 1)
- },
- orderDetail(){
- if (!this.formData.id) {
- return
- }
- getDetail(this.formData.id).then(res => {
- this.formData = res.data
- this.fileList = JSON.parse(this.formData.imgs)
- for (let itemNum in res.data.greenRecyclingItemList) {
- let nowItem = res.data.greenRecyclingItemList[itemNum]
- console.info(nowItem)
- let nowGoods = this.goodsList.findIndex(item => item.productName === nowItem.productName)
- console.info(nowGoods)
- if (nowGoods !== -1) {
- this.goodsList[nowGoods].productQuantity = nowItem.productQuantity
- this.goodsList[nowGoods].productPrice = nowItem.productPrice
- continue
- }
- this.goodsList.push(nowItem)
- }
- })
- },
- clickSubmit() {
- if (this.totalAmount <= 0) return;
- this.formData.quantity = this.goodsList.reduce((sum, item) => sum + item.productQuantity, 0);
- this.formData.amount = this.totalAmount
- this.formData.greenRecyclingItemList = this.goodsList.filter(item => item.productQuantity > 0)
- this.formData.imgs = JSON.stringify(this.fileList)
- if (this.formData.id) {
- this.handleUpdate()
- return;
- }
- this.handleSubmit()
- },
- handleSubmit() {
- uni.showLoading({title: '提交中...'});
- submitRecycling(this.formData).then(res => {
- console.info(res)
- this.formData.id = res.data
- uni.showToast({title: '提交成功'});
- }).finally(() => {
- uni.hideLoading();
- })
- },
- handleUpdate() {
- uni.showLoading({title: '提交中...'});
- this.formData.greenRecyclingItemList = this.goodsList.filter(item => item.productQuantity > 0)
- updateRecycling(this.formData).then(res => {
- console.info(res)
- // this.formData.id = res.data
- uni.showToast({title: '修改成功'});
- }).finally(() => {
- uni.hideLoading();
- })
- },
- uploadFilePromise(url) {
- return new Promise((resolve, reject) => {
- let a = uni.uploadFile({
- url: http.config.baseURL +
- '/blade-resource/oss/endpoint/put-file', // 仅为示例,非真实的接口地址
- filePath: url,
- name: 'file',
- formData: {
- user: 'test'
- },
- header: {
- // 客户端认证参数
- 'Authorization': 'Basic ' + Base64.encode(clientId + ':' +
- clientSecret),
- 'Blade-Auth': 'bearer ' + uni.getStorageSync('accessToken')
- },
- success: (res) => {
- setTimeout(() => {
- resolve(res.data)
- }, 1000)
- }
- });
- })
- },
- getGoodsList(){
- greenRecyclingGoods().then(res => {
- for(let num in res.data){
- this.goodsList.push( {
- productName: res.data[num].dictValue,
- productPrice: res.data[num].dictKey,
- productQuantity: 0
- })
- }
- this.orderDetail()
- })
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .container {
- padding: 20rpx;
- background-color: #f8f8f8;
- /* 按钮高度 + 边距 */
- padding-bottom: 120rpx;
- }
- .goods-list {
- height: 30vh;
- margin-bottom: 20rpx;
- background-color: #fff;
- border-radius: 16rpx;
- padding: 20rpx;
- }
- .goods-list::-webkit-scrollbar {
- display: none;
- width: 0;
- height: 0;
- }
- .goods-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 20rpx 0;
- border-bottom: 1rpx solid #eee;
- }
- .goods-info {
- flex: 1;
- display: flex;
- flex-direction: column;
- }
- .goods-name {
- font-size: 28rpx;
- color: #333;
- }
- .goods-price {
- font-size: 32rpx;
- color: #f56c6c;
- margin-top: 10rpx;
- }
- .quantity-box {
- width: 200rpx;
- }
- .form-section {
- background-color: #fff;
- border-radius: 16rpx;
- padding: 30rpx;
- margin-top: 20rpx;
- margin-bottom: 20rpx;
- }
- .section-title {
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- margin-bottom: 20rpx;
- position: relative;
- padding-left: 16rpx;
- }
- .section-title::before {
- content: "";
- position: absolute;
- left: 0;
- top: 50%;
- transform: translateY(-50%);
- width: 4rpx;
- height: 24rpx;
- background-color: #2979ff;
- border-radius: 2rpx;
- }
- .remark-textarea {
- background-color: #f8f8f8;
- border-radius: 12rpx;
- padding: 20rpx;
- font-size: 28rpx;
- }
- .placeholder-style {
- color: #b2b2b2;
- font-size: 26rpx;
- }
- .upload-area {
- margin-top: 20rpx;
- }
- .upload-tip {
- color: #999;
- font-size: 26rpx;
- text-align: center;
- padding: 20rpx 0;
- }
- /* 调整计数器样式 */
- /deep/ .u-textarea__count {
- color: #999;
- font-size: 24rpx;
- }
- /* 美化已上传图片的样式 */
- /deep/ .u-upload__wrap__preview__image {
- border-radius: 8rpx;
- }
- /deep/ .u-upload__wrap__preview__mask {
- background-color: rgba(0, 0, 0, 0.3);
- border-radius: 8rpx;
- }
- .quantity-section {
- display: flex;
- align-items: center;
- gap: 20rpx;
- }
- .amount-box {
- display: flex;
- align-items: baseline;
- min-width: 140rpx;
- padding: 10rpx 20rpx;
- background-color: #f8f8f8;
- border-radius: 8rpx;
- justify-content: flex-end;
- }
- .amount-symbol {
- font-size: 24rpx;
- color: #f56c6c;
- margin-right: 4rpx;
- }
- .amount-number {
- font-size: 28rpx;
- color: #f56c6c;
- font-weight: 500;
- }
- .total-section {
- background-color: #fff;
- border-radius: 16rpx;
- padding: 30rpx;
- margin-top: 20rpx;
- display: flex;
- justify-content: space-between;
- align-items: center;
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
- }
- .total-amount-box {
- display: flex;
- align-items: baseline;
- }
- .total-symbol {
- font-size: 28rpx;
- color: #f56c6c;
- margin-right: 6rpx;
- }
- .total-number {
- font-size: 36rpx;
- color: #f56c6c;
- font-weight: bold;
- }
- .total-label {
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- }
- .submit-section {
- position: fixed;
- bottom: 30rpx;
- left: 0;
- right: 0;
- padding: 0 30rpx;
- }
- .submit-btn {
- width: 100%;
- height: 80rpx;
- background: linear-gradient(90deg, #2979ff, #4facfe);
- color: white;
- font-size: 32rpx;
- border-radius: 40rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 4rpx 12rpx rgba(41, 121, 255, 0.3);
- &[disabled] {
- background: #c8c9cc;
- box-shadow: none;
- }
- }
- /* 调整输入框样式 */
- /deep/ .u-input__content__field-wrapper__field {
- font-size: 28rpx !important;
- color: #333 !important;
- }
- /* 搜索框区域样式 */
- .search-box {
- padding: 20rpx;
- background: #fff;
- border-bottom: 1rpx solid #f0f0f0;
- }
- /* 空状态提示 */
- .empty-tip {
- padding-top: 100rpx;
- }
- /* 顶部操作栏(渐变背景) */
- .picker-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 24rpx 32rpx;
- background: linear-gradient(135deg, #2979ff 0%, #4facfe 100%);
- box-shadow: 0 4rpx 12rpx rgba(41, 121, 255, 0.2);
- .header-title {
- font-size: 34rpx;
- font-weight: 500;
- color: #fff;
- letter-spacing: 1rpx;
- }
- .header-btn {
- font-size: 30rpx;
- color: rgba(255,255,255,0.9);
- padding: 8rpx 16rpx;
- border-radius: 40rpx;
- &.confirm {
- background: rgba(255,255,255,0.2);
- }
- }
- }
- /* 客户列表(卡片式设计) */
- .customer-list {
- background-color: #f8f8f8;
- padding: 20rpx;
- }
- .customer-card {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 28rpx 32rpx;
- margin: 0 20rpx 20rpx;
- background: #fff;
- border-radius: 16rpx;
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
- transition: all 0.3s ease;
- &:active {
- transform: scale(0.98);
- }
- &.active {
- border: 1rpx solid #2979ff;
- box-shadow: 0 4rpx 16rpx rgba(41, 121, 255, 0.3);
- }
- .customer-name {
- font-size: 32rpx;
- color: #333;
- flex: 1;
- }
- .check-mark {
- width: 44rpx;
- height: 44rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(41, 121, 255, 0.1);
- border-radius: 50%;
- }
- }
- .card-content {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 28rpx 32rpx;
- .customer-name {
- font-size: 32rpx;
- color: #333;
- font-weight: 500;
- letter-spacing: 0.5rpx;
- }
- .check-mark {
- width: 44rpx;
- height: 44rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(41, 121, 255, 0.1);
- border-radius: 50%;
- }
- }
- .card-divider {
- height: 1rpx;
- background: linear-gradient(90deg, transparent, rgba(0,0,0,0.08), transparent);
- margin: 0 32rpx;
- }
- </style>
|