This commit is contained in:
2026-04-13 18:37:28 +08:00
parent e661e13833
commit 32c7fb6e9a
4 changed files with 132 additions and 101 deletions
+1
View File
@@ -68,6 +68,7 @@ func main() {
models.ConfigAllInit() models.ConfigAllInit()
routers.ApiUserInit() //用户表先初始化这是必须的因为后面需要用到用户组 routers.ApiUserInit() //用户表先初始化这是必须的因为后面需要用到用户组
routers.ApiScheduleInit() routers.ApiScheduleInit()
routers.ApiPurchaseInit()
//创建必要目录 //创建必要目录
for _, path := range models.ConfigsFile.Pahts { for _, path := range models.ConfigsFile.Pahts {
-33
View File
@@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/glebarez/sqlite" "github.com/glebarez/sqlite"
"gorm.io/datatypes"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
@@ -90,34 +89,6 @@ type APIRequestLog_ struct {
CreatedAt time.Time `gorm:"column:created_at;type:datetime;default:CURRENT_TIMESTAMP" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;default:CURRENT_TIMESTAMP" json:"created_at"`
} }
type TabPurchaseOrder struct {
ID uint `gorm:"primarykey"`
UserID uint `gorm:"not null"`
Title string `gorm:"size:200;comment:标题"`
Remark string `gorm:"type:text;comment:备注"`
Photos datatypes.JSON `gorm:"type:json;comment:照片哈希数组"`
Link string `gorm:"size:1000;comment:链接"`
PartName string `gorm:"size:200;not null;comment:物品名称"`
Styles string `gorm:"type:text;comment:样式数组"`
//Costs datatypes.JSON `gorm:"type:json;comment:费用明细数组"`
UpdateTime *time.Time `gorm:"type:datetime;autoUpdateTime;comment:更新时间"`
TrackingNumber string `gorm:"size:100;Index;comment:快递单号"`
OrderStatus string `gorm:"default:1;comment:订单状态"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
UpdatedAt *time.Time `gorm:"type:datetime;autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}
type TabPurchaseCosts struct {
ID uint `gorm:"primarykey"`
OrderID uint `gorm:"not null"`
UserID uint `gorm:"not null"`
Price int `gorm:"not null"`
Quantity int `gorm:"not null"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
}
func DatabaseInit() error { func DatabaseInit() error {
var err error var err error
fmt.Println("database_init") fmt.Println("database_init")
@@ -159,9 +130,5 @@ func DatabaseInit() error {
DB.AutoMigrate(&APIRequestLog_{}) DB.AutoMigrate(&APIRequestLog_{})
DB.AutoMigrate(&TabPurchaseOrder{})
DB.AutoMigrate(&TabPurchaseCosts{})
return nil return nil
} }
+84 -38
View File
@@ -1,13 +1,13 @@
package routers package routers
import ( import (
"encoding/json"
"fmt" "fmt"
"ops/models" "ops/models"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"gorm.io/datatypes" "gorm.io/gorm"
) )
type CostItem struct { type CostItem struct {
@@ -18,16 +18,51 @@ type CostItem struct {
Type string `json:"type"` // 费用类型 Type string `json:"type"` // 费用类型
} }
type From_purchase_addorder struct { type From_purchase_addorder struct {
Costs []CostItem `json:"costs"` // 成本 Costs []CostItem `json:"costs"` // 成本
Link string `json:"link"` // 链接 Link string `json:"link"` // 链接
OrderStatus string `json:"order_status"` // 订单状态 OrderStatus string `json:"order_status"` // 订单状态
PartName string `json:"partname"` // 物件名称 //PartName string `json:"partname"` // 物件名称
Photos []string `json:"photos"` // 图片备注 Photos []string `json:"photos"` // 图片备注
Remark string `json:"remark"` // 备注 Remark string `json:"remark"` // 备注
Styles string `json:"styles"` // 样式备注 Styles string `json:"styles"` // 样式备注
Title string `json:"title"` // 标题 Title string `json:"title"` // 标题
TrackingNumber string `json:"tracking_number"` // 快递单号 //TrackingNumber string `json:"tracking_number"` // 快递单号
UpdateTime string `json:"update_time"` // 更新时间 //UpdateTime string `json:"update_time"` // 更新时间
}
type TabPurchaseOrder struct {
ID uint `gorm:"primarykey"`
UserID uint `gorm:"not null"`
Title string `gorm:"size:200;comment:标题"`
Remark string `gorm:"type:text;comment:备注"`
Link string `gorm:"size:1000;comment:链接"`
Styles string `gorm:"type:text;comment:样式数组"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}
type TabPurchaseCosts struct {
ID uint `gorm:"primarykey"`
OrderID uint `gorm:"not null"`
UserID uint `gorm:"not null"`
Price int `gorm:"not null"`
Quantity int `gorm:"not null"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
}
type TabPurchaseFileBind struct {
ID uint `gorm:"primarykey"`
OrderID uint `gorm:"not null"`
FileID uint `gorm:"not null"`
CreatedAt *time.Time `gorm:"type:datetime;autoCreateTime"`
}
func ApiPurchaseInit() {
models.DB.AutoMigrate(&TabPurchaseOrder{})
models.DB.AutoMigrate(&TabPurchaseCosts{})
models.DB.AutoMigrate(&TabPurchaseFileBind{})
} }
func ApiPurchase(r *gin.RouterGroup) { func ApiPurchase(r *gin.RouterGroup) {
@@ -61,11 +96,11 @@ func ApiPurchase(r *gin.RouterGroup) {
//读取有多少条目 //读取有多少条目
var count int64 var count int64
models.DB.Model(&models.TabPurchaseOrder{}).Count(&count) models.DB.Model(TabPurchaseOrder{}).Count(&count)
//fmt.Println(count) //fmt.Println(count)
//读取条目 //读取条目
var getorders []models.TabPurchaseOrder var getorders []TabPurchaseOrder
models.DB.Order("created_at DESC").Offset(jsondata.Entries * (jsondata.Page - 1)).Limit(jsondata.Entries).Find(&getorders) models.DB.Order("created_at DESC").Offset(jsondata.Entries * (jsondata.Page - 1)).Limit(jsondata.Entries).Find(&getorders)
ReturnJson(ctx, "apiOK", map[string]interface{}{ ReturnJson(ctx, "apiOK", map[string]interface{}{
@@ -97,8 +132,8 @@ func ApiPurchase(r *gin.RouterGroup) {
var jsondata From_purchase_addorder var jsondata From_purchase_addorder
if err := mapstructure.Decode(data, &jsondata); err == nil { if err := mapstructure.Decode(data, &jsondata); err == nil {
jsonStr, _ := json.MarshalIndent(jsondata, "", " ") //jsonStr, _ := json.MarshalIndent(jsondata, "", " ")
fmt.Println("转换后数据:\n", string(jsonStr)) //fmt.Println("转换后数据:\n", string(jsonStr))
//数据比较混乱 在这里校验 //数据比较混乱 在这里校验
@@ -106,19 +141,15 @@ func ApiPurchase(r *gin.RouterGroup) {
is_data_ok := true is_data_ok := true
if jsondata.Title == "" { if jsondata.Title == "" {
is_data_ok = false is_data_ok = false
fmt.Println("err1")
} }
//判断数量与价格是否为负数 //判断数量与价格是否为负数
for i := 0; i < len(jsondata.Costs); i++ { for i := 0; i < len(jsondata.Costs); i++ {
if jsondata.Costs[i].Cost <= 0 { if jsondata.Costs[i].Cost <= 0 {
is_data_ok = false is_data_ok = false
fmt.Println("err2")
} }
if jsondata.Costs[i].Int <= 0 { if jsondata.Costs[i].Int <= 0 {
is_data_ok = false is_data_ok = false
fmt.Println("err3")
} }
} }
@@ -133,33 +164,30 @@ func ApiPurchase(r *gin.RouterGroup) {
} }
//判断时间字符串是否合法 //判断时间字符串是否合法
uptime, e := models.StringToTimePtr(jsondata.UpdateTime) // uptime, e := models.StringToTimePtr(jsondata.UpdateTime)
if e != nil { // if e != nil {
is_data_ok = false // is_data_ok = false
fmt.Println("err5") // fmt.Println("err5")
} // }
if is_data_ok { if is_data_ok {
//校验通过 //校验通过
//载入数据库 //载入数据库
photos, _ := json.Marshal(jsondata.Photos) //把图片数组转换成字符串 //fmt.Println("yes")
new_data := models.TabPurchaseOrder{
UserID: user.ID, //photos, _ := json.Marshal(jsondata.Photos) //把图片数组转换成字符串
Title: jsondata.Title, new_data := TabPurchaseOrder{
Remark: jsondata.Remark, UserID: user.ID,
Photos: datatypes.JSON(photos), Title: jsondata.Title,
Link: jsondata.Link, Remark: jsondata.Remark,
PartName: jsondata.PartName, Link: jsondata.Link,
Styles: jsondata.Styles, Styles: jsondata.Styles,
UpdateTime: uptime,
TrackingNumber: jsondata.TrackingNumber,
OrderStatus: jsondata.OrderStatus,
} }
models.DB.Create(&new_data) models.DB.Create(&new_data)
for i := 0; i < len(jsondata.Costs); i++ { for i := 0; i < len(jsondata.Costs); i++ {
new_cost_data := models.TabPurchaseCosts{ new_cost_data := TabPurchaseCosts{
Price: jsondata.Costs[i].Cost, Price: jsondata.Costs[i].Cost,
Quantity: jsondata.Costs[i].Int, Quantity: jsondata.Costs[i].Int,
UserID: user.ID, UserID: user.ID,
@@ -168,6 +196,24 @@ func ApiPurchase(r *gin.RouterGroup) {
models.DB.Create(&new_cost_data) models.DB.Create(&new_cost_data)
} }
//绑定文件
for i := 0; i < len(jsondata.Photos); i++ {
findFile := models.TabFileInfo_{
Sha256: jsondata.Photos[i],
Type: "image",
}
if models.DB.Where(&findFile).First(&findFile).Error == nil {
bind := TabPurchaseFileBind{
OrderID: new_data.ID,
FileID: findFile.ID,
}
models.DB.Create(&bind)
}
}
ReturnJson(ctx, "apiOK", nil)
} else { } else {
ReturnJson(ctx, "jsonErr_1", nil) ReturnJson(ctx, "jsonErr_1", nil)
} }
@@ -115,6 +115,9 @@ const newCost = reactive({
currencyType: "1", // 货币类型:默认人民币 currencyType: "1", // 货币类型:默认人民币
}); });
// 费用验证错误状态:点击添加按钮后发现费用为0时触发
const costError = ref(false);
// ==================== 表单数据 ==================== // ==================== 表单数据 ====================
/** /**
* 订单表单主数据对象 * 订单表单主数据对象
@@ -125,12 +128,12 @@ const form = reactive({
remark: "", // 备注说明 remark: "", // 备注说明
photos: [], // 图片列表(上传后由 dropzone 填充) photos: [], // 图片列表(上传后由 dropzone 填充)
link: "", // 采购链接 link: "", // 采购链接
partname: "", // 配件名称 //partname: "", // 配件名称
styles: "", // 款式标签 styles: "", // 款式标签
costs: [], // 费用明细(提交前由 costEntries 转换) costs: [], // 费用明细(提交前由 costEntries 转换)
tracking_number: "", // 快递单号 //tracking_number: "", // 快递单号
updatetime: "", // 更新时间 //updatetime: "", // 更新时间
order_status: "1", // 订单状态(默认待下单) //order_status: "1", // 订单状态(默认待下单)
}); });
/** /**
@@ -146,7 +149,11 @@ const newCostTotal = computed(() =>
* 条件:单价必须大于0 * 条件:单价必须大于0
*/ */
function addCostEntry() { function addCostEntry() {
if (newCost.cost <= 0) return; if (newCost.cost <= 0) {
costError.value = true;
return;
}
costError.value = false;
costEntries.push({ costEntries.push({
type: newCost.type, type: newCost.type,
int: newCost.int, int: newCost.int,
@@ -178,6 +185,8 @@ watch(
(val) => { (val) => {
const fixed = parseFloat(val).toFixed(2); const fixed = parseFloat(val).toFixed(2);
if (parseFloat(fixed) !== val) newCost.cost = parseFloat(fixed); if (parseFloat(fixed) !== val) newCost.cost = parseFloat(fixed);
// 用户开始输入时清除错误状态
if (val > 0) costError.value = false;
}, },
); );
@@ -205,10 +214,11 @@ async function handleSubmit() {
// 从 dropzone 组件获取已上传的图片文件名 // 从 dropzone 组件获取已上传的图片文件名
form.photos = []; form.photos = [];
if (photosRef.value?.has_some_files) { // if (photosRef.value?.has_some_files) {
const result = photosRef.value.get_some_files(); // const result = photosRef.value.get_some_files();
form.photos = result.map((f) => f.name); // form.photos = result.map((f) => f.name);
} // }
form.photos=photosRef.value?.return_files().map((f)=>f.hash);
// 将费用明细转换为提交格式 // 将费用明细转换为提交格式
// 注意:金额需要从"元"转为"分"(乘以100)存储 // 注意:金额需要从"元"转为"分"(乘以100)存储
@@ -219,23 +229,29 @@ async function handleSubmit() {
})); }));
// 开始 loading // 开始 loading
loading.value = true; console.log(form)
try { purchaseApi.addOrder(form).then((r)=>{
// 调用采购 API 添加订单
const { errCode } = await purchaseApi.addOrder(form); console.log(r)
if (errCode === 0) {
// 保存成功,显示成功提示 })
toast.success(t("message.save_ok")); // loading.value = true;
} else { // try {
// 服务器错误,显示错误提示 // // 调用采购 API 添加订单
toast.error(t("message.server_error")); // const { errCode } = await purchaseApi.addOrder(form);
} // if (errCode === 0) {
} catch { // // 保存成功,显示成功提示
// 错误已被 HTTP 拦截器处理,此处无需额外处理 // toast.success(t("message.save_ok"));
} finally { // } else {
// 无论成功失败,都要关闭 loading // // 服务器错误,显示错误提示
loading.value = false; // toast.error(t("message.server_error"));
} // }
// } catch {
// // 错误已被 HTTP 拦截器处理,此处无需额外处理
// } finally {
// // 无论成功失败,都要关闭 loading
// loading.value = false;
// }
} }
</script> </script>
@@ -371,11 +387,11 @@ async function handleSubmit() {
<td class="px-3 py-2 text-gray-500">{{ item.cost }}</td> <td class="px-3 py-2 text-gray-500">{{ item.cost }}</td>
<td class="px-3 py-2 text-gray-500">{{ item.costt }}</td> <td class="px-3 py-2 text-gray-500">{{ item.costt }}</td>
<td class="px-3 py-2 text-gray-500"> <td class="px-3 py-2 text-gray-500">
<img <!-- <img
:src="currencyFlags[item.currencytype]" :src="currencyFlags[item.currencytype]"
class="inline-block h-4 w-6 align-middle" class="inline-block h-4 w-6 align-middle"
:alt="currencyOptions[item.currencytype]" :alt="currencyOptions[item.currencytype]"
/> /> -->
{{ currencyOptions[item.currencytype] }} {{ currencyOptions[item.currencytype] }}
</td> </td>
<td class="px-3 py-2"> <td class="px-3 py-2">
@@ -430,7 +446,8 @@ async function handleSubmit() {
<input <input
v-model="newCost.cost" v-model="newCost.cost"
type="number" type="number"
class="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm dark:border-dk-muted dark:bg-dk-base dark:text-white" class="w-full rounded-lg border bg-white px-3 py-2 text-sm dark:bg-dk-base dark:text-white"
:class="costError ? 'border-red-500' : 'border-gray-300 dark:border-dk-muted'"
step="0.01" step="0.01"
min="0" min="0"
/> />
@@ -479,7 +496,7 @@ async function handleSubmit() {
<useDropzone <useDropzone
acceptFiles="image/*" acceptFiles="image/*"
uploadURL="/api/files/upload/image" uploadURL="/api/files/upload/image"
maxFiles="10" :maxFiles="10"
ref="photosRef" ref="photosRef"
/> />
</div> </div>