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