diff --git a/src/backend/items/admin.py b/src/backend/items/admin.py
index b4a05c6..f6152ad 100644
--- a/src/backend/items/admin.py
+++ b/src/backend/items/admin.py
@@ -1,20 +1,35 @@
from django.contrib import admin
-from .models import Item, ItemUsage, Category
+from .models import Item, ItemUsage, Category, ItemImage, UsageImage
+
+
+class ItemImageInline(admin.TabularInline):
+ """物品图片内联编辑"""
+ model = ItemImage
+ extra = 1
+ fields = ['image', 'description', 'is_primary']
+
+
+class UsageImageInline(admin.TabularInline):
+ """使用记录图片内联编辑"""
+ model = UsageImage
+ extra = 1
+ fields = ['image', 'image_type', 'description']
@admin.register(Item)
class ItemAdmin(admin.ModelAdmin):
- list_display = ['name', 'serial_number', 'category', 'status', 'location', 'created_at']
+ list_display = ['name', 'serial_number', 'category', 'status', 'location', 'owner', 'created_at']
list_filter = ['status', 'category', 'created_at']
- search_fields = ['name', 'serial_number', 'description']
+ search_fields = ['name', 'serial_number', 'description', 'owner']
readonly_fields = ['created_at', 'updated_at']
+ inlines = [ItemImageInline]
fieldsets = (
('基本信息', {
'fields': ('name', 'description', 'serial_number', 'category')
}),
('状态和位置', {
- 'fields': ('status', 'location')
+ 'fields': ('status', 'location', 'owner')
}),
('购买信息', {
'fields': ('purchase_date', 'value')
@@ -28,16 +43,17 @@ class ItemAdmin(admin.ModelAdmin):
@admin.register(ItemUsage)
class ItemUsageAdmin(admin.ModelAdmin):
- list_display = ['item', 'user', 'start_time', 'end_time', 'is_returned', 'purpose']
+ list_display = ['item', 'user', 'borrower_contact', 'start_time', 'end_time', 'is_returned', 'purpose']
list_filter = ['is_returned', 'start_time', 'item__category']
- search_fields = ['item__name', 'user__username', 'purpose']
+ search_fields = ['item__name', 'user', 'purpose', 'borrower_contact']
readonly_fields = ['created_at']
+ inlines = [UsageImageInline]
fieldsets = (
('使用信息', {
- 'fields': ('item', 'user', 'purpose', 'notes')
+ 'fields': ('item', 'user', 'borrower_contact', 'purpose', 'notes')
}),
('时间信息', {
- 'fields': ('start_time', 'end_time', 'is_returned')
+ 'fields': ('start_time', 'end_time', 'expected_return_time', 'is_returned')
}),
('状况记录', {
'fields': ('condition_before', 'condition_after')
@@ -49,6 +65,22 @@ class ItemUsageAdmin(admin.ModelAdmin):
)
+@admin.register(ItemImage)
+class ItemImageAdmin(admin.ModelAdmin):
+ list_display = ['item', 'description', 'is_primary', 'created_at']
+ list_filter = ['is_primary', 'created_at']
+ search_fields = ['item__name', 'description']
+ readonly_fields = ['created_at']
+
+
+@admin.register(UsageImage)
+class UsageImageAdmin(admin.ModelAdmin):
+ list_display = ['usage', 'image_type', 'description', 'created_at']
+ list_filter = ['image_type', 'created_at']
+ search_fields = ['usage__item__name', 'usage__user', 'description']
+ readonly_fields = ['created_at']
+
+
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'description', 'created_at']
diff --git a/src/backend/items/management/__init__.py b/src/backend/items/management/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/backend/items/management/__init__.py
@@ -0,0 +1 @@
+
diff --git a/src/backend/items/management/commands/__init__.py b/src/backend/items/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/backend/items/management/commands/create_default_categories.py b/src/backend/items/management/commands/create_default_categories.py
new file mode 100644
index 0000000..0234194
--- /dev/null
+++ b/src/backend/items/management/commands/create_default_categories.py
@@ -0,0 +1,143 @@
+from django.core.management.base import BaseCommand
+from items.models import Category
+
+# 添加物品类别:python manage.py create_default_categories
+
+
+class Command(BaseCommand):
+ help = '创建默认的物品类别'
+
+ def handle(self, *args, **options):
+ # 默认物品类别列表
+ default_categories = [
+ {
+ 'name': '电子设备',
+ 'description': '包括电脑、手机、平板等电子产品'
+ },
+ {
+ 'name': '办公用品',
+ 'description': '办公桌椅、文具、打印机等办公设备'
+ },
+ {
+ 'name': '实验器材',
+ 'description': '实验室设备、仪器、工具等'
+ },
+ {
+ 'name': '家具',
+ 'description': '桌子、椅子、柜子等家具用品'
+ },
+ {
+ 'name': '图书资料',
+ 'description': '书籍、文档、资料等'
+ },
+ {
+ 'name': '工具设备',
+ 'description': '维修工具、测量仪器等'
+ },
+ {
+ 'name': '音响设备',
+ 'description': '音响、麦克风、投影仪等音视频设备'
+ },
+ {
+ 'name': '运动器材',
+ 'description': '体育用品、健身器材等'
+ },
+ {
+ 'name': '清洁用品',
+ 'description': '清洁工具、清洁剂等'
+ },
+ {
+ 'name': '网络设备',
+ 'description': '路由器、交换机、网线等网络相关设备'
+ },
+ {
+ 'name': '安全设备',
+ 'description': '监控摄像头、门禁系统、报警器等安全设备'
+ },
+ {
+ 'name': '医疗用品',
+ 'description': '急救包、体温计、血压计等医疗相关用品'
+ },
+ {
+ 'name': '厨房用具',
+ 'description': '微波炉、咖啡机、餐具等厨房设备'
+ },
+ {
+ 'name': '照明设备',
+ 'description': '台灯、吊灯、应急灯等照明用品'
+ },
+ {
+ 'name': '存储设备',
+ 'description': '硬盘、U盘、移动硬盘等存储设备'
+ },
+ {
+ 'name': '车辆工具',
+ 'description': '汽车配件、维修工具、车载设备等'
+ },
+ {
+ 'name': '服装用品',
+ 'description': '工作服、防护服、鞋帽等服装类物品'
+ },
+ {
+ 'name': '教学用品',
+ 'description': '黑板、白板、教学模型等教学设备'
+ },
+ {
+ 'name': '通讯设备',
+ 'description': '对讲机、电话、传真机等通讯设备'
+ },
+ {
+ 'name': '空调制冷',
+ 'description': '空调、风扇、加湿器等温度调节设备'
+ },
+ {
+ 'name': '消防设备',
+ 'description': '灭火器、烟雾报警器、消防栓等消防用品'
+ },
+ {
+ 'name': '软件许可',
+ 'description': '软件授权、许可证、数字资产等'
+ },
+ {
+ 'name': '包装材料',
+ 'description': '纸箱、胶带、包装袋等包装用品'
+ },
+ {
+ 'name': '园艺用品',
+ 'description': '花盆、园艺工具、肥料等园艺相关用品'
+ },
+ {
+ 'name': '其他',
+ 'description': '其他未分类的物品'
+ }
+ ]
+
+ created_count = 0
+ updated_count = 0
+
+ for category_data in default_categories:
+ category, created = Category.objects.get_or_create(
+ name=category_data['name'],
+ defaults={'description': category_data['description']}
+ )
+
+ if created:
+ created_count += 1
+ self.stdout.write(
+ self.style.SUCCESS(f'创建类别: {category.name}')
+ )
+ else:
+ # 更新描述(如果不同)
+ if category.description != category_data['description']:
+ category.description = category_data['description']
+ category.save()
+ updated_count += 1
+ self.stdout.write(
+ self.style.WARNING(f'更新类别: {category.name}')
+ )
+
+ self.stdout.write(
+ self.style.SUCCESS(
+ f'\n任务完成!创建了 {created_count} 个新类别,更新了 {updated_count} 个类别。'
+ )
+ )
diff --git a/src/backend/items/models.py b/src/backend/items/models.py
index e2e832b..cc28303 100644
--- a/src/backend/items/models.py
+++ b/src/backend/items/models.py
@@ -1,4 +1,27 @@
from django.db import models
+import os
+import uuid
+
+
+def get_item_image_path(instance, filename):
+ """生成物品图片的存储路径"""
+ # 如果物品还没有ID,先生成一个临时文件夹名
+ item_id = instance.item.id if instance.item.id else str(uuid.uuid4())
+ return f'items/{item_id}/initial/{filename}'
+
+
+def get_usage_borrow_image_path(instance, filename):
+ """生成借用时图片的存储路径"""
+ item_id = instance.usage.item.id if instance.usage.item.id else str(uuid.uuid4())
+ usage_id = instance.usage.id if instance.usage.id else str(uuid.uuid4())
+ return f'items/{item_id}/usage/{usage_id}/borrow/{filename}'
+
+
+def get_usage_return_image_path(instance, filename):
+ """生成归还时图片的存储路径"""
+ item_id = instance.usage.item.id if instance.usage.item.id else str(uuid.uuid4())
+ usage_id = instance.usage.id if instance.usage.id else str(uuid.uuid4())
+ return f'items/{item_id}/usage/{usage_id}/return/{filename}'
class Item(models.Model):
@@ -34,6 +57,23 @@ class Item(models.Model):
return f"{self.name} ({self.serial_number})"
+class ItemImage(models.Model):
+ """物品图片模型"""
+ item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='images', verbose_name='物品')
+ image = models.ImageField(upload_to=get_item_image_path, verbose_name='图片')
+ description = models.CharField(max_length=200, blank=True, verbose_name='图片描述')
+ is_primary = models.BooleanField(default=False, verbose_name='是否为主图')
+ created_at = models.DateTimeField(auto_now_add=True, verbose_name='上传时间')
+
+ class Meta:
+ verbose_name = '物品图片'
+ verbose_name_plural = '物品图片'
+ ordering = ['-is_primary', '-created_at']
+
+ def __str__(self):
+ return f"{self.item.name} - 图片"
+
+
class ItemUsage(models.Model):
"""物品使用记录模型"""
item = models.ForeignKey(Item, on_delete=models.CASCADE, verbose_name='物品')
@@ -58,6 +98,53 @@ class ItemUsage(models.Model):
return f"{self.item.name} - {self.user} ({self.start_time.strftime('%Y-%m-%d %H:%M')})"
+class UsageImage(models.Model):
+ """使用记录图片模型"""
+ IMAGE_TYPE_CHOICES = [
+ ('borrow', '借用时'),
+ ('return', '归还时'),
+ ]
+
+ usage = models.ForeignKey(ItemUsage, on_delete=models.CASCADE, related_name='images', verbose_name='使用记录')
+ image = models.ImageField(upload_to='usage_images/', verbose_name='图片')
+ image_type = models.CharField(max_length=10, choices=IMAGE_TYPE_CHOICES, verbose_name='图片类型')
+ description = models.CharField(max_length=200, blank=True, verbose_name='图片描述')
+ created_at = models.DateTimeField(auto_now_add=True, verbose_name='上传时间')
+
+ def save(self, *args, **kwargs):
+ super().save(*args, **kwargs)
+ # 保存后重新组织文件路径
+ if self.image and self.usage_id and self.usage.item_id:
+ import os
+ import shutil
+ from django.conf import settings
+
+ old_path = self.image.path
+ if self.image_type == 'borrow':
+ new_relative_path = f'items/{self.usage.item_id}/usage/{self.usage_id}/borrow/{os.path.basename(old_path)}'
+ else:
+ new_relative_path = f'items/{self.usage.item_id}/usage/{self.usage_id}/return/{os.path.basename(old_path)}'
+
+ new_path = os.path.join(settings.MEDIA_ROOT, new_relative_path)
+
+ # 创建目录
+ os.makedirs(os.path.dirname(new_path), exist_ok=True)
+
+ # 移动文件
+ if os.path.exists(old_path) and old_path != new_path:
+ shutil.move(old_path, new_path)
+ self.image.name = new_relative_path
+ super().save(update_fields=['image'])
+
+ class Meta:
+ verbose_name = '使用记录图片'
+ verbose_name_plural = '使用记录图片'
+ ordering = ['-created_at']
+
+ def __str__(self):
+ return f"{self.usage.item.name} - {self.get_image_type_display()}"
+
+
class Category(models.Model):
"""物品类别模型"""
name = models.CharField(max_length=50, unique=True, verbose_name='类别名称')
diff --git a/src/backend/items/serializers.py b/src/backend/items/serializers.py
index e9fc02c..5b22206 100644
--- a/src/backend/items/serializers.py
+++ b/src/backend/items/serializers.py
@@ -1,7 +1,7 @@
from django.contrib.auth.models import User
from rest_framework import serializers
-from .models import Item, ItemUsage, Category
+from .models import Item, ItemUsage, Category, ItemImage, UsageImage
class UserSerializer(serializers.ModelSerializer):
@@ -16,15 +16,47 @@ class CategorySerializer(serializers.ModelSerializer):
fields = ['id', 'name', 'description', 'created_at']
+class ItemImageSerializer(serializers.ModelSerializer):
+ """物品图片序列化器"""
+ image_url = serializers.SerializerMethodField()
+
+ class Meta:
+ model = ItemImage
+ fields = ['id', 'image', 'image_url', 'description', 'is_primary', 'created_at']
+
+ def get_image_url(self, obj):
+ request = self.context.get('request')
+ if obj.image and request:
+ return request.build_absolute_uri(obj.image.url)
+ return None
+
+
+class UsageImageSerializer(serializers.ModelSerializer):
+ """使用记录图片序列化器"""
+ image_url = serializers.SerializerMethodField()
+
+ class Meta:
+ model = UsageImage
+ fields = ['id', 'image', 'image_url', 'image_type', 'description', 'created_at']
+
+ def get_image_url(self, obj):
+ request = self.context.get('request')
+ if obj.image and request:
+ return request.build_absolute_uri(obj.image.url)
+ return None
+
+
class ItemSerializer(serializers.ModelSerializer):
current_user = serializers.SerializerMethodField()
+ images = ItemImageSerializer(many=True, read_only=True)
+ primary_image = serializers.SerializerMethodField()
class Meta:
model = Item
fields = [
'id', 'name', 'description', 'serial_number', 'category', 'status',
'location', 'owner', 'purchase_date', 'value', 'created_at', 'updated_at',
- 'current_user'
+ 'current_user', 'images', 'primary_image'
]
def get_current_user(self, obj):
@@ -39,20 +71,43 @@ class ItemSerializer(serializers.ModelSerializer):
}
return None
+ def get_primary_image(self, obj):
+ """获取主图片"""
+ primary_image = obj.images.filter(is_primary=True).first()
+ if primary_image:
+ request = self.context.get('request')
+ if request:
+ return request.build_absolute_uri(primary_image.image.url)
+ return None
+
class ItemUsageSerializer(serializers.ModelSerializer):
item_name = serializers.CharField(source='item.name', read_only=True)
item_serial = serializers.CharField(source='item.serial_number', read_only=True)
+ images = UsageImageSerializer(many=True, read_only=True)
+ borrow_images = serializers.SerializerMethodField()
+ return_images = serializers.SerializerMethodField()
class Meta:
model = ItemUsage
fields = [
'id', 'item', 'item_name', 'item_serial', 'user', 'borrower_contact',
'start_time', 'end_time', 'purpose', 'notes', 'is_returned',
- 'condition_before', 'condition_after', 'expected_return_time', 'created_at'
+ 'condition_before', 'condition_after', 'expected_return_time', 'created_at',
+ 'images', 'borrow_images', 'return_images'
]
read_only_fields = ['created_at']
+ def get_borrow_images(self, obj):
+ """获取借用时图片"""
+ borrow_images = obj.images.filter(image_type='borrow')
+ return UsageImageSerializer(borrow_images, many=True, context=self.context).data
+
+ def get_return_images(self, obj):
+ """获取归还时图片"""
+ return_images = obj.images.filter(image_type='return')
+ return UsageImageSerializer(return_images, many=True, context=self.context).data
+
class ItemDetailSerializer(ItemSerializer):
"""物品详情序列化器,包含使用历史"""
@@ -64,4 +119,4 @@ class ItemDetailSerializer(ItemSerializer):
def get_usage_history(self, obj):
"""获取物品的使用历史"""
usages = ItemUsage.objects.filter(item=obj).order_by('-start_time')[:10]
- return ItemUsageSerializer(usages, many=True).data
+ return ItemUsageSerializer(usages, many=True, context=self.context).data
diff --git a/src/backend/items/urls.py b/src/backend/items/urls.py
index 1061e8f..fae589d 100644
--- a/src/backend/items/urls.py
+++ b/src/backend/items/urls.py
@@ -8,6 +8,8 @@ router.register(r'items', views.ItemViewSet)
router.register(r'usages', views.ItemUsageViewSet)
router.register(r'item_categories', views.CategoryViewSet)
router.register(r'users', views.UserViewSet)
+router.register(r'item-images', views.ItemImageViewSet)
+router.register(r'usage-images', views.UsageImageViewSet)
urlpatterns = [
path('api/', include(router.urls)),
diff --git a/src/backend/items/views.py b/src/backend/items/views.py
index 08285a8..5dfafa6 100644
--- a/src/backend/items/views.py
+++ b/src/backend/items/views.py
@@ -4,17 +4,19 @@ from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTAuthentication
+from rest_framework.parsers import MultiPartParser, FormParser
-from .models import Item, ItemUsage, Category
+from .models import Item, ItemUsage, Category, ItemImage, UsageImage
from .serializers import (
ItemSerializer, ItemDetailSerializer, ItemUsageSerializer,
- CategorySerializer, UserSerializer
+ CategorySerializer, UserSerializer, ItemImageSerializer, UsageImageSerializer
)
class ItemViewSet(viewsets.ModelViewSet):
"""物品管理API"""
authentication_classes = [JWTAuthentication]
+ parser_classes = [MultiPartParser, FormParser]
queryset = Item.objects.all()
serializer_class = ItemSerializer
@@ -23,6 +25,80 @@ class ItemViewSet(viewsets.ModelViewSet):
return ItemDetailSerializer
return ItemSerializer
+ def create(self, request, *args, **kwargs):
+ """创建物品,强制要求上传至少一张图片"""
+ # 检查是否上传了图片
+ images = request.FILES.getlist('images')
+ if not images:
+ return Response(
+ {'error': '创建物品时必须上传至少一张图片'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ # 创建物品
+ serializer = self.get_serializer(data=request.data)
+ serializer.is_valid(raise_exception=True)
+ item = serializer.save()
+
+ # 保存图片
+ for i, image in enumerate(images):
+ ItemImage.objects.create(
+ item=item,
+ image=image,
+ description=request.data.get(f'image_descriptions[{i}]', ''),
+ is_primary=(i == 0) # 第一张图片设为主图
+ )
+
+ # 返回包含图片的完整数据
+ response_serializer = ItemDetailSerializer(item, context={'request': request})
+ return Response(response_serializer.data, status=status.HTTP_201_CREATED)
+
+ @action(detail=True, methods=['post'])
+ def upload_images(self, request, pk=None):
+ """上传物品图片"""
+ item = self.get_object()
+ images = request.FILES.getlist('images')
+
+ if not images:
+ return Response(
+ {'error': '请选择要上传的图片'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ uploaded_images = []
+ for i, image in enumerate(images):
+ item_image = ItemImage.objects.create(
+ item=item,
+ image=image,
+ description=request.data.get(f'image_descriptions[{i}]', ''),
+ is_primary=False
+ )
+ uploaded_images.append(item_image)
+
+ serializer = ItemImageSerializer(uploaded_images, many=True, context={'request': request})
+ return Response(serializer.data)
+
+ @action(detail=True, methods=['post'])
+ def set_primary_image(self, request, pk=None):
+ """设置主图片"""
+ item = self.get_object()
+ image_id = request.data.get('image_id')
+
+ try:
+ # 取消当前主图
+ item.images.filter(is_primary=True).update(is_primary=False)
+ # 设置新主图
+ new_primary = item.images.get(id=image_id)
+ new_primary.is_primary = True
+ new_primary.save()
+
+ return Response({'message': '主图设置成功'})
+ except ItemImage.DoesNotExist:
+ return Response(
+ {'error': '图片不存在'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
@action(detail=True, methods=['post'])
def borrow(self, request, pk=None):
"""借用物品"""
@@ -57,11 +133,22 @@ class ItemViewSet(viewsets.ModelViewSet):
condition_before=condition_before
)
+ # 保存借用时的图片
+ borrow_images = request.FILES.getlist('borrow_images')
+ for i, image in enumerate(borrow_images):
+ UsageImage.objects.create(
+ usage=usage,
+ image=image,
+ image_type='borrow',
+ description=request.data.get(f'borrow_image_descriptions[{i}]', '')
+ )
+
# 更新物品状态
item.status = 'in_use'
item.save()
- return Response(ItemUsageSerializer(usage).data)
+ response_serializer = ItemUsageSerializer(usage, context={'request': request})
+ return Response(response_serializer.data)
@action(detail=True, methods=['post'])
def return_item(self, request, pk=None):
@@ -86,11 +173,22 @@ class ItemViewSet(viewsets.ModelViewSet):
current_usage.notes = request.data.get('return_notes', current_usage.notes)
current_usage.save()
+ # 保存归还时的图片
+ return_images = request.FILES.getlist('return_images')
+ for i, image in enumerate(return_images):
+ UsageImage.objects.create(
+ usage=current_usage,
+ image=image,
+ image_type='return',
+ description=request.data.get(f'return_image_descriptions[{i}]', '')
+ )
+
# 更新物品状态
item.status = 'available'
item.save()
- return Response(ItemUsageSerializer(current_usage).data)
+ response_serializer = ItemUsageSerializer(current_usage, context={'request': request})
+ return Response(response_serializer.data)
@action(detail=False)
def available(self, request):
@@ -110,9 +208,36 @@ class ItemViewSet(viewsets.ModelViewSet):
class ItemUsageViewSet(viewsets.ModelViewSet):
"""使用记录管理API"""
authentication_classes = [JWTAuthentication]
+ parser_classes = [MultiPartParser, FormParser]
queryset = ItemUsage.objects.all()
serializer_class = ItemUsageSerializer
+ @action(detail=True, methods=['post'])
+ def upload_images(self, request, pk=None):
+ """上传使用记录图片"""
+ usage = self.get_object()
+ images = request.FILES.getlist('images')
+ image_type = request.data.get('image_type', 'borrow')
+
+ if not images:
+ return Response(
+ {'error': '请选择要上传的图片'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ uploaded_images = []
+ for i, image in enumerate(images):
+ usage_image = UsageImage.objects.create(
+ usage=usage,
+ image=image,
+ image_type=image_type,
+ description=request.data.get(f'image_descriptions[{i}]', '')
+ )
+ uploaded_images.append(usage_image)
+
+ serializer = UsageImageSerializer(uploaded_images, many=True, context={'request': request})
+ return Response(serializer.data)
+
@action(detail=False)
def current(self, request):
"""获取当前使用中的记录"""
@@ -143,3 +268,19 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
authentication_classes = [JWTAuthentication]
queryset = User.objects.all()
serializer_class = UserSerializer
+
+
+class ItemImageViewSet(viewsets.ModelViewSet):
+ """物品图片管理API"""
+ authentication_classes = [JWTAuthentication]
+ parser_classes = [MultiPartParser, FormParser]
+ queryset = ItemImage.objects.all()
+ serializer_class = ItemImageSerializer
+
+
+class UsageImageViewSet(viewsets.ModelViewSet):
+ """使用记录图片管理API"""
+ authentication_classes = [JWTAuthentication]
+ parser_classes = [MultiPartParser, FormParser]
+ queryset = UsageImage.objects.all()
+ serializer_class = UsageImageSerializer
diff --git a/src/fronted/src/router/index.js b/src/fronted/src/router/index.js
index 13d8a7f..2522121 100644
--- a/src/fronted/src/router/index.js
+++ b/src/fronted/src/router/index.js
@@ -2,6 +2,7 @@ import {createRouter, createWebHistory} from 'vue-router'
import Login from '../views/Login.vue'
import ItemList from '../views/ItemList.vue'
import ItemDetail from '../views/ItemDetail.vue'
+import ItemCreate from '../views/ItemCreate.vue'
import ItemUsage from '../views/ItemUsage.vue'
import Dashboard from '../views/Dashboard.vue'
import FinanceDashboard from '../views/FinanceDashboard.vue'
@@ -28,6 +29,11 @@ const routes = [
name: 'ItemList',
component: ItemList
},
+ {
+ path: '/items/create',
+ name: 'ItemCreate',
+ component: ItemCreate
+ },
{
path: '/items/:id',
name: 'ItemDetail',
diff --git a/src/fronted/src/services/api.js b/src/fronted/src/services/api.js
index ecdf45f..788f5d0 100644
--- a/src/fronted/src/services/api.js
+++ b/src/fronted/src/services/api.js
@@ -113,9 +113,13 @@ export const itemService = {
return apiClient.get(`/items/${id}/`)
},
- // 创建新物品
- createItem(item) {
- return apiClient.post('/items/', item)
+ // 创建新物品(支持图片上传)
+ createItem(formData) {
+ return apiClient.post('/items/', formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
},
// 更新物品
@@ -128,6 +132,31 @@ export const itemService = {
return apiClient.delete(`/items/${id}/`)
},
+ // 上传物品图片
+ uploadItemImages(itemId, formData) {
+ return apiClient.post(`/items/${itemId}/upload_images/`, formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
+ },
+
+ // 设置主图片
+ setPrimaryImage(itemId, imageId) {
+ const formData = new FormData()
+ formData.append('image_id', imageId)
+ return apiClient.post(`/items/${itemId}/set_primary_image/`, formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
+ },
+
+ // 删除物品图片
+ deleteItemImage(itemId, imageId) {
+ return apiClient.delete(`/item-images/${imageId}/`)
+ },
+
// 获取可用物品
getAvailableItems() {
return apiClient.get('/items/available/')
@@ -138,14 +167,22 @@ export const itemService = {
return apiClient.get('/items/in_use/')
},
- // 借用物品
- borrowItem(itemId, borrowData) {
- return apiClient.post(`/items/${itemId}/borrow/`, borrowData)
+ // 借用物品(支持图片上传)
+ borrowItem(itemId, formData) {
+ return apiClient.post(`/items/${itemId}/borrow/`, formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
},
- // 归还物品
- returnItem(itemId, returnData) {
- return apiClient.post(`/items/${itemId}/return_item/`, returnData)
+ // 归还物品(支持图片上传)
+ returnItem(itemId, formData) {
+ return apiClient.post(`/items/${itemId}/return_item/`, formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
}
}
@@ -173,6 +210,15 @@ export const usageService = {
// 更新使用记录
updateUsage(id, usage) {
return apiClient.put(`/usages/${id}/`, usage)
+ },
+
+ // 上传使用记录图片
+ uploadUsageImages(usageId, formData) {
+ return apiClient.post(`/usages/${usageId}/upload_images/`, formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
}
}
diff --git a/src/fronted/src/views/ItemCreate.vue b/src/fronted/src/views/ItemCreate.vue
new file mode 100644
index 0000000..da991fc
--- /dev/null
+++ b/src/fronted/src/views/ItemCreate.vue
@@ -0,0 +1,290 @@
+
+ 创建新物品
+
{{ image.description }}
+