完善前端项目结构和依赖配置
- 更新 package.json 中的依赖版本号,包括 element-plus、axios、pinia、vue-router 等 - 删除 HelloWorld 组件,替换为 router-view 以支持路由功能 - 添加 auth、category、customer、dashboard、medicine、role、stock、stockIn、stockOut、 supplier、user 等多个 API 接口文件 - 创建权限指令、主布局组件和全局样式 - 集成 Element Plus UI 框架并注册图标组件 - 实现应用状态管理和用户状态管理 Store - 配置 Vue Router 路由系统,支持动态菜单路由生成 - 移除旧的 CSS 样式文件,改用 SCSS 全局样式 ```
This commit is contained in:
86
frontend/src/views/stockOut/StockOutForm.vue
Normal file
86
frontend/src/views/stockOut/StockOutForm.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { getAllCustomers } from '@/api/customer'
|
||||
import { getStockList } from '@/api/stock'
|
||||
import { createStockOut } from '@/api/stockOut'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const customerOptions = ref([])
|
||||
const stockOptions = ref([])
|
||||
const form = reactive({ customerId: null, warehouseId: 1, outDate: new Date().toISOString().slice(0, 10), outType: 1, remark: '' })
|
||||
const details = ref([])
|
||||
|
||||
function addDetail() {
|
||||
details.value.push({ medicineId: null, batchNo: '', quantity: 1, unitPrice: 0 })
|
||||
}
|
||||
function removeDetail(i) { details.value.splice(i, 1) }
|
||||
function detailTotal(d) { return (d.quantity || 0) * (d.unitPrice || 0) }
|
||||
const totalAmount = ref(0)
|
||||
function updateTotal() { totalAmount.value = details.value.reduce((s, d) => s + detailTotal(d), 0) }
|
||||
|
||||
async function loadOptions() {
|
||||
try {
|
||||
const [cRes, sRes] = await Promise.all([getAllCustomers(), getStockList({ page: 1, size: 999 })])
|
||||
customerOptions.value = cRes.data || []
|
||||
stockOptions.value = sRes.data?.records || []
|
||||
} catch { /* */ }
|
||||
}
|
||||
|
||||
function onMedicineSelected(index, stockId) {
|
||||
const stock = stockOptions.value.find(s => s.id === stockId)
|
||||
if (stock) {
|
||||
details.value[index].medicineId = stock.medicineId
|
||||
details.value[index].batchNo = stock.batchNo
|
||||
details.value[index].unitPrice = stock.costPrice || 0
|
||||
details.value[index]._maxQty = stock.quantity
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!form.customerId) { ElMessage.warning('请选择客户'); return }
|
||||
if (!details.value.length) { ElMessage.warning('请添加药品明细'); return }
|
||||
updateTotal()
|
||||
await createStockOut({ ...form, details: details.value, totalAmount: totalAmount.value })
|
||||
ElMessage.success('出库单已创建,等待审核')
|
||||
router.push('/stock-out/list')
|
||||
}
|
||||
|
||||
onMounted(loadOptions)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<h3 style="margin-bottom:16px">新建出库单</h3>
|
||||
<el-form :model="form" label-width="90px" style="max-width:700px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="客户" required><el-select v-model="form.customerId" placeholder="选择客户" filterable style="width:100%"><el-option v-for="c in customerOptions" :key="c.id" :label="c.name" :value="c.id" /></el-select></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="出库类型"><el-select v-model="form.outType" style="width:100%"><el-option label="销售出库" :value="1" /><el-option label="领用出库" :value="2" /><el-option label="报损出库" :value="3" /><el-option label="退货出库" :value="4" /></el-select></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="出库日期" required><el-date-picker v-model="form.outDate" type="date" style="width:100%" /></el-form-item></el-col>
|
||||
<el-col :span="24"><el-form-item label="备注"><el-input v-model="form.remark" type="textarea" :rows="2" /></el-form-item></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<div style="margin:20px 0"><el-button type="primary" @click="addDetail">+ 添加药品</el-button><span style="margin-left:12px;color:#909399">总计: ¥{{ totalAmount }}</span></div>
|
||||
<el-table :data="details" border>
|
||||
<el-table-column label="库存批次" min-width="220">
|
||||
<template #default="{row, $index}">
|
||||
<el-select :model-value="row._stockId" placeholder="选择药品批次" filterable style="width:100%" @change="onMedicineSelected($index, $event)">
|
||||
<el-option v-for="s in stockOptions" :key="s.id" :label="`${s.medicineName} - ${s.batchNo} (库存${s.quantity})`" :value="s.id" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="批号" width="120"><template #default="{row}">{{row.batchNo}}</template></el-table-column>
|
||||
<el-table-column label="数量" width="100"><template #default="{row}"><el-input-number v-model="row.quantity" :min="1" :max="row._maxQty||9999" size="small" style="width:100%" @change="updateTotal" /></template></el-table-column>
|
||||
<el-table-column label="单价" width="110"><template #default="{row}"><el-input-number v-model="row.unitPrice" :min="0" :precision="2" size="small" style="width:100%" @change="updateTotal" /></template></el-table-column>
|
||||
<el-table-column label="小计" width="110"><template #default="{row}">¥{{detailTotal(row)}}</template></el-table-column>
|
||||
<el-table-column label="操作" width="80"><template #default="{ $index }"><el-button size="small" type="danger" @click="removeDetail($index)">移除</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div style="margin-top:20px">
|
||||
<el-button @click="router.back()">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">提交出库单</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
55
frontend/src/views/stockOut/StockOutList.vue
Normal file
55
frontend/src/views/stockOut/StockOutList.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { getStockOutList, auditStockOut } from '@/api/stockOut'
|
||||
import { Search, Refresh, Plus } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const query = reactive({ page: 1, size: 10 })
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
function statusTag(s) { return { 0: 'warning', 1: 'success', 2: 'info' }[s] || 'info' }
|
||||
function statusText(s) { return { 0: '待审核', 1: '已出库', 2: '已取消' }[s] || '未知' }
|
||||
function typeText(s) { return { 1: '销售出库', 2: '领用出库', 3: '报损出库', 4: '退货出库' }[s] || '未知' }
|
||||
|
||||
async function loadData() { try { const r = await getStockOutList(query); tableData.value = r.data?.records || []; total.value = r.data?.total || 0 } catch { /* */ } }
|
||||
function handleCreate() { router.push('/stock-out/add') }
|
||||
function handleView(row) { ElMessage.info('出库单号: ' + (row.recordNo || row.record_no)) }
|
||||
async function handleAudit(row) { await auditStockOut(row.id, { status: 1 }); ElMessage.success('审核通过,库存已扣减'); loadData() }
|
||||
|
||||
onMounted(loadData)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="search-bar">
|
||||
<el-input v-model="query.recordNo" placeholder="出库单号" clearable style="width:180px" />
|
||||
<el-button type="primary" :icon="Search" @click="loadData">搜索</el-button>
|
||||
<el-button :icon="Refresh" @click="Object.keys(query).forEach(k=>query[k]=k==='page'?1:k==='size'?10:'');loadData()">重置</el-button>
|
||||
</div>
|
||||
<div class="tool-bar">
|
||||
<el-button type="primary" v-permission="'stock-out:add'" :icon="Plus" @click="handleCreate">+ 新建出库单</el-button>
|
||||
</div>
|
||||
<el-table :data="tableData" border stripe>
|
||||
<el-table-column prop="recordNo" label="出库单号" width="150" />
|
||||
<el-table-column prop="customerName" label="客户" min-width="160" />
|
||||
<el-table-column prop="outType" label="类型" width="100"><template #default="{row}">{{typeText(row.outType)}}</template></el-table-column>
|
||||
<el-table-column prop="totalAmount" label="总金额" width="110"><template #default="{row}">¥{{row.totalAmount}}</template></el-table-column>
|
||||
<el-table-column prop="outDate" label="出库日期" width="120" />
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="{row}"><el-tag :type="statusTag(row.status)" size="small">{{statusText(row.status)}}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template #default="{row}">
|
||||
<div class="table-actions">
|
||||
<el-button size="small" @click="handleView(row)">查看</el-button>
|
||||
<el-button v-if="row.status===0" size="small" type="success" v-permission="'stock-out:audit'" @click="handleAudit(row)">审核</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="display:flex;justify-content:flex-end;margin-top:16px"><el-pagination v-model:current-page="query.page" v-model:page-size="query.size" :total="total" :page-sizes="[10,20,50]" layout="total,sizes,prev,pager,next" @current-change="loadData" @size-change="query.page=1;loadData()" /></div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user