feat: update

This commit is contained in:
Czw 2023-10-20 18:05:32 +08:00
parent 54c2d98859
commit 60289060d5
466 changed files with 1 additions and 49708 deletions

View file

@ -1,47 +0,0 @@
import request from "@/utils/request";
// 生产计划
export function productionOrderList(params) {
return request({ url: `/production_orders/`, method: "get", params });
}
export function productionOrderDetail(params) {
return request({ url: `/production_orders/${params.id}/`, method: "get", params });
}
export function productionOrderCreate(data) {
return request({ url: `/production_orders/`, method: "post", data });
}
export function productionOrderUpdate(data) {
return request({ url: `/production_orders/${data.id}/`, method: "put", data });
}
export function productionOrderDelete(data) {
return request({ url: `/production_orders/${data.id}/`, method: "delete", data });
}
export function productionOrderNumber(params) {
return request({ url: `/production_orders/number/`, method: "get", params });
}
export function productionOrderIssue(data) {
return request({ url: `/production_orders/${data.id}/issue/`, method: "post", data });
}
export function productionOrderClose(data) {
return request({ url: `/production_orders/${data.id}/close/`, method: "post", data });
}
// 生产记录
export function productionRecordList(params) {
return request({ url: `/production_records/`, method: "get", params });
}
export function productionRecordDetail(params) {
return request({ url: `/production_records/${params.id}/`, method: "get", params });
}
export function productionRecordCreate(data) {
return request({ url: `/production_records/`, method: "post", data });
}

View file

@ -1,28 +0,0 @@
export default {
path: '/production',
name: 'production',
component: () => import('@/layouts/BaseLayout'),
redirect: '/production/plan',
children: [
{
path: 'plan',
meta: { title: '生产计划' },
component: () => import('@/views/production/productionPlan/index'),
},
{
path: 'detial',
meta: { title: '生产计划详情' },
component: () => import('@/views/production/productionDetial/index'),
},
{
path: 'task',
meta: { title: '生产任务' },
component: () => import('@/views/production/productionTask/index'),
},
{
path: 'record',
meta: { title: '生产记录' },
component: () => import('@/views/production/productionRecord/index'),
},
],
}

View file

@ -1,100 +0,0 @@
<template>
<div>
<a-card title="生产计划详情">
<a-button slot="extra" type="primary" style="margin-right: 8px;" ghost v-print="'#printContent'">
<a-icon type="printer" />打印</a-button
>
<a-button
slot="extra"
type="primary"
ghost
@click="
() => {
this.$router.go(-1);
}
"
>
<a-icon type="left" />返回</a-button
>
<section id="printContent">
<a-spin :spinning="loading">
<img id="barcode" style="float: right" />
<a-descriptions bordered>
<a-descriptions-item label="生产计划单号">
{{ item.number }}
</a-descriptions-item>
<a-descriptions-item label="销售单号">
{{ item.sales_order_number }}
</a-descriptions-item>
<a-descriptions-item label="状态">
{{ item.status_display }}
</a-descriptions-item>
<a-descriptions-item label="产品编号">
{{ item.goods_number }}
</a-descriptions-item>
<a-descriptions-item label="产品名称">
{{ item.goods_name }}
</a-descriptions-item>
<a-descriptions-item label="计划数量">
{{ item.total_quantity }}
</a-descriptions-item>
<a-descriptions-item label="完成数量">
{{ item.quantity_produced }}
</a-descriptions-item>
<a-descriptions-item label="计划开始时间">
{{ item.start_time }}
</a-descriptions-item>
<a-descriptions-item label="计划结束时间">
{{ item.end_time }}
</a-descriptions-item>
<a-descriptions-item label="创建时间">
{{ item.create_time }}
</a-descriptions-item>
<a-descriptions-item label="创建人">
{{ item.creator_name }}
</a-descriptions-item>
</a-descriptions>
</a-spin>
</section>
</a-card>
</div>
</template>
<script>
import { productionOrderDetail } from "@/api/production";
import JsBarcode from "jsbarcode";
export default {
data() {
return {
loading: false,
item: {},
};
},
methods: {
getJsBarcode(number) {
JsBarcode("#barcode", number, {
lineColor: "#000",
width: 2,
height: 40,
displayValue: true,
});
},
initData() {
this.loading = true;
productionOrderDetail({ id: this.$route.query.id })
.then((data) => {
this.item = data;
this.getJsBarcode(data.number);
})
.finally(() => {
this.loading = false;
});
},
},
mounted() {
this.initData();
},
};
</script>
<style></style>

View file

@ -1,129 +0,0 @@
<template>
<div>
<a-modal v-model="visible" :confirmLoading="loading" :maskClosable="false" @cancel="cancel" @ok="confirm">
<div slot="title">{{ form.id ? "编辑生产计划" : "新增生产计划" }}</div>
<div>
<a-form-model ref="form" :model="dataForm" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-model-item prop="number" label="生产单号">
<a-input v-model="dataForm.number" />
</a-form-model-item>
<a-form-model-item prop="is_related" label="类型">
<a-radio-group v-model="dataForm.is_related" button-style="solid" @change="switchType">
<a-radio-button :value="false">按库存生产</a-radio-button>
<a-radio-button :value="true">按订单生产</a-radio-button>
</a-radio-group>
</a-form-model-item>
<a-form-model-item v-if="!dataForm.is_related" prop="goods" label="产品">
<goods-select v-model="dataForm.goods" :defaultItem="{ ...dataForm }" />
</a-form-model-item>
<a-form-model-item v-if="dataForm.is_related" prop="sales_order" label="销售单">
<sales-order-select
v-model="dataForm.sales_order"
:defaultItem="{ ...dataForm }"
@select="
(item) => {
dataForm.sales_goods_items = item.sales_goods_items;
dataForm.goods = undefined;
}
"
/>
</a-form-model-item>
<a-form-model-item v-if="dataForm.is_related" prop="goods" label="产品">
<a-select v-model="dataForm.goods" style="width: 100%;">
<a-select-option v-for="item in dataForm.sales_goods_items" :key="item.goods" :value="item.goods">
{{ item.goods_name }}
</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item prop="total_quantity" label="计划数量">
<a-input-number v-model="dataForm.total_quantity" style="width: 100%;" />
</a-form-model-item>
<a-form-model-item prop="start_time" label="开始时间">
<a-date-picker
v-model="dataForm.start_time"
placeholder="请选择时间"
valueFormat="YYYY-MM-DD HH:mm:ss"
show-time
style="width: 100%;"
/>
</a-form-model-item>
<a-form-model-item prop="end_time" label="结束时间">
<a-date-picker
v-model="dataForm.end_time"
placeholder="请选择时间"
valueFormat="YYYY-MM-DD HH:mm:ss"
show-time
style="width: 100%;"
/>
</a-form-model-item>
</a-form-model>
</div>
</a-modal>
</div>
</template>
<script>
import { productionOrderCreate, productionOrderUpdate, productionOrderNumber } from "@/api/production";
export default {
components: {
GoodsSelect: () => import("@/components/GoodsSelect/index"),
SalesOrderSelect: () => import("@/components/SalesOrderSelect/index"),
},
props: ["visible", "form"],
model: { prop: "visible", event: "cancel" },
data() {
return {
rules: {
number: [{ required: true, message: "请输入编号", trigger: "change" }],
goods: [{ required: true, message: "请选择生产产品", trigger: "change" }],
total_quantity: [{ required: true, message: "请输入计划数量", trigger: "change" }],
},
loading: false,
dataForm: {},
};
},
methods: {
confirm() {
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true;
let func = this.dataForm.id ? productionOrderUpdate : productionOrderCreate;
func(this.dataForm)
.then((data) => {
this.$message.success(this.form.id ? "修改成功" : "新增成功");
this.$emit(this.form.id ? "update" : "create", data);
this.cancel();
})
.finally(() => {
this.loading = false;
});
}
});
},
cancel() {
this.$emit("cancel", false);
this.$refs.form.resetFields();
},
switchType() {
this.dataForm = { ...this.dataForm, sales_order: undefined, goods: undefined };
},
},
watch: {
visible(value) {
if (value) {
if (this.form.id) {
this.dataForm = { ...this.form };
} else {
this.dataForm = { is_related: false };
productionOrderNumber().then((data) => {
this.dataForm = { ...this.dataForm, number: data.number };
});
}
}
},
},
};
</script>
<style scoped></style>

View file

@ -1,207 +0,0 @@
<template>
<div>
<a-card title="生产计划">
<a-row :gutter="[12, 8]">
<a-col :span="24" style="width: 256px;">
<a-range-picker @change="onChangePicker" />
</a-col>
<a-col :span="24" style="width: 200px;">
<a-input v-model="searchForm.search" placeholder="生产单号, 销售单号" allowClear @pressEnter="search" />
</a-col>
<a-col :span="24" style="width: 84px;">
<a-button type="primary" icon="search" @click="search">查询</a-button>
</a-col>
<div style="margin-bottom: 12px; float: right">
<a-button type="primary" icon="plus" style="margin: 0 8px" @click="openCreateModal({})">
新增生产计划
</a-button>
</div>
</a-row>
<a-row style="margin-top: 12px">
<a-table
rowKey="id"
:columns="columns"
:data-source="items"
:pagination="pagination"
:loading="loading"
:scroll="{ x: 1600 }"
>
<div slot="action" slot-scope="value, item, index">
<a-button-group size="small">
<a-button v-if="item.status == 'in_plan'" @click="openCreateModal(item)">编辑</a-button>
<a-button @click="detial(item)">详情</a-button>
<a-popconfirm v-if="item.status == 'in_plan'" title="确定发布吗?" @confirm="issue(item)">
<a-button type="primary">发布工单</a-button>
</a-popconfirm>
<a-popconfirm v-if="item.status == 'in_progress'" title="确定关闭吗?" @confirm="close(item)">
<a-button type="primary">关闭工单</a-button>
</a-popconfirm>
<a-popconfirm v-if="item.status == 'in_plan'" title="确定删除吗?" @confirm="destroy(item)">
<a-button type="danger">删除</a-button>
</a-popconfirm>
</a-button-group>
</div>
</a-table>
</a-row>
</a-card>
<form-modal v-model="visible" :form="targetItem" @create="create" @update="update" />
</div>
</template>
<script>
import {
productionOrderList,
productionOrderDelete,
productionOrderIssue,
productionOrderClose,
} from "@/api/production";
export default {
components: {
FormModal: () => import("./FormModal.vue"),
},
data() {
return {
searchForm: { search: "", page: 1, ordering: undefined },
pagination: { current: 1, total: 0, pageSize: 16 },
loading: false,
items: [],
columns: [
{
title: "序号",
dataIndex: "index",
width: 60,
fixed: "left",
customRender: (value, item, index) => {
return index + 1;
},
},
{
title: "生产计划单号",
dataIndex: "number",
fixed: "left",
},
{
title: "销售单号",
dataIndex: "sales_order_number",
},
{
title: "状态",
dataIndex: "status_display",
width: 100,
},
{
title: "产品编号",
dataIndex: "goods_number",
},
{
title: "产品名称",
dataIndex: "goods_name",
},
{
title: "计划数量",
dataIndex: "total_quantity",
width: 100,
},
{
title: "完成数量",
dataIndex: "quantity_produced",
width: 100,
},
{
title: "计划开始时间",
dataIndex: "start_time",
width: 180,
},
{
title: "计划结束时间",
dataIndex: "end_time",
width: 180,
},
{
title: "操作",
dataIndex: "action",
fixed: "right",
width: 256,
scopedSlots: { customRender: "action" },
},
],
visible: false,
targetItem: {},
};
},
methods: {
initialize() {
this.list();
},
list() {
this.loading = true;
productionOrderList(this.searchForm)
.then((data) => {
this.pagination.total = data.count;
this.items = data.results;
})
.finally(() => {
this.loading = false;
});
},
create(item) {
this.items = this.$functions.insertItem(this.items, item);
},
update(item) {
this.items = this.$functions.replaceItem(this.items, item);
},
destroy(item) {
productionOrderDelete({ id: item.id }).then(() => {
this.$message.success("删除成功");
this.items = this.$functions.removeItem(this.items, item);
});
},
issue(item) {
productionOrderIssue({ id: item.id }).then((data) => {
this.$message.success("发布成功");
this.items = this.$functions.replaceItem(this.items, data);
});
},
close(item) {
productionOrderClose({ id: item.id }).then((data) => {
this.$message.success("关闭成功");
this.items = this.$functions.replaceItem(this.items, data);
});
},
detial(item) {
this.$router.push({ path: "/production/detial", query: { id: item.id } });
},
search() {
this.searchForm.page = 1;
this.pagination.current = 1;
this.list();
},
tableChange(pagination, filters, sorter) {
this.searchForm.page = pagination.current;
this.pagination.current = pagination.current;
this.searchForm.ordering = `${sorter.order == "descend" ? "-" : ""}${sorter.field}`;
this.list();
},
onChangePicker(date, dateString) {
let startDate = date[0];
let endDate = date[1];
this.searchForm.start_date = startDate ? startDate.format("YYYY-MM-DD") : undefined;
this.searchForm.end_date = endDate ? endDate.add(1, "days").format("YYYY-MM-DD") : undefined;
this.search();
},
openCreateModal(item) {
this.targetItem = { ...item };
this.visible = true;
},
},
mounted() {
this.initialize();
},
};
</script>
<style scoped></style>

View file

@ -1,124 +0,0 @@
<template>
<div>
<a-card title="生产记录">
<a-row :gutter="[12, 8]">
<a-col :span="24" style="width: 256px;">
<a-range-picker @change="onChangePicker" />
</a-col>
<a-col :span="24" style="width: 200px;">
<a-input v-model="searchForm.search" placeholder="生产单号, 销售单号" allowClear @pressEnter="search" />
</a-col>
<a-col :span="24" style="width: 84px;">
<a-button type="primary" icon="search" @click="search">查询</a-button>
</a-col>
</a-row>
<a-row style="margin-top: 12px">
<a-table
rowKey="id"
:columns="columns"
:data-source="items"
:pagination="pagination"
:loading="loading"
>
</a-table>
</a-row>
</a-card>
</div>
</template>
<script>
import { productionRecordList } from "@/api/production";
export default {
data() {
return {
searchForm: { search: "", page: 1, ordering: undefined },
pagination: { current: 1, total: 0, pageSize: 16 },
loading: false,
items: [],
columns: [
{
title: "序号",
dataIndex: "index",
width: 60,
fixed: "left",
customRender: (value, item, index) => {
return index + 1;
},
},
{
title: "生产计划单号",
dataIndex: "production_order_number",
fixed: "left",
},
{
title: "产品编号",
dataIndex: "goods_number",
},
{
title: "产品名称",
dataIndex: "goods_name",
},
{
title: "生产数量",
dataIndex: "production_quantity",
width: 100,
},
{
title: "创建时间",
dataIndex: "create_time",
width: 180,
},
{
title: "创建人",
dataIndex: "creator_name",
width: 180,
},
],
};
},
methods: {
initialize() {
this.list();
},
list() {
this.loading = true;
productionRecordList(this.searchForm)
.then((data) => {
this.pagination.total = data.count;
this.items = data.results;
})
.finally(() => {
this.loading = false;
});
},
create() {
this.list();
},
search() {
this.searchForm.page = 1;
this.pagination.current = 1;
this.list();
},
tableChange(pagination, filters, sorter) {
this.searchForm.page = pagination.current;
this.pagination.current = pagination.current;
this.searchForm.ordering = `${sorter.order == "descend" ? "-" : ""}${sorter.field}`;
this.list();
},
onChangePicker(date, dateString) {
let startDate = date[0];
let endDate = date[1];
this.searchForm.start_date = startDate ? startDate.format("YYYY-MM-DD") : undefined;
this.searchForm.end_date = endDate ? endDate.add(1, "days").format("YYYY-MM-DD") : undefined;
this.search();
},
},
mounted() {
this.initialize();
},
};
</script>
<style scoped></style>

View file

@ -1,66 +0,0 @@
<template>
<div>
<a-modal v-model="visible" :confirmLoading="loading" :maskClosable="false" @cancel="cancel" @ok="confirm">
<div slot="title">生产</div>
<div>
<a-form-model ref="form" :model="dataForm" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-model-item prop="production_quantity" label="生产数量">
<a-input-number v-model="dataForm.production_quantity" style="width: 100%;" />
</a-form-model-item>
</a-form-model>
</div>
</a-modal>
</div>
</template>
<script>
import { productionRecordCreate } from "@/api/production";
export default {
props: ["visible", "form"],
model: { prop: "visible", event: "cancel" },
data() {
return {
rules: {
production_quantity: [{ required: true, message: "请输入生产数量", trigger: "change" }],
},
loading: false,
dataForm: {},
};
},
methods: {
confirm() {
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true;
productionRecordCreate(this.dataForm)
.then((data) => {
this.$message.success("生产成功");
this.$emit("create", data);
this.cancel();
})
.finally(() => {
this.loading = false;
});
}
});
},
cancel() {
this.$emit("cancel", false);
this.$refs.form.resetFields();
},
},
watch: {
visible(value) {
if (value) {
this.dataForm = {
production_order: this.form.id,
production_quantity: this.form.remain_quantity,
}
}
},
},
};
</script>
<style scoped></style>

View file

@ -1,161 +0,0 @@
<template>
<div>
<a-card title="生产任务">
<a-row :gutter="[12, 8]">
<a-col :span="24" style="width: 256px;">
<a-range-picker @change="onChangePicker" />
</a-col>
<a-col :span="24" style="width: 200px;">
<a-input v-model="searchForm.search" placeholder="生产单号, 销售单号" allowClear @pressEnter="search" />
</a-col>
<a-col :span="24" style="width: 84px;">
<a-button type="primary" icon="search" @click="search">查询</a-button>
</a-col>
</a-row>
<a-row style="margin-top: 12px">
<a-table
rowKey="id"
:columns="columns"
:data-source="items"
:pagination="pagination"
:loading="loading"
:scroll="{ x: 1600 }"
>
<div slot="action" slot-scope="value, item, index">
<a-button-group size="small">
<a-button @click="detial()">详情</a-button>
<a-button type="primary" @click="openCreateModal(item)">生产</a-button>
</a-button-group>
</div>
</a-table>
</a-row>
</a-card>
<form-modal v-model="visible" :form="targetItem" @create="create" />
</div>
</template>
<script>
import { productionOrderList } from "@/api/production";
export default {
components: {
FormModal: () => import("./FormModal.vue"),
},
data() {
return {
searchForm: { search: "", page: 1, ordering: undefined, status: "in_progress" },
pagination: { current: 1, total: 0, pageSize: 16 },
loading: false,
items: [],
columns: [
{
title: "序号",
dataIndex: "index",
width: 60,
fixed: "left",
customRender: (value, item, index) => {
return index + 1;
},
},
{
title: "生产计划单号",
dataIndex: "number",
fixed: "left",
},
{
title: "销售单号",
dataIndex: "sales_order_number",
},
{
title: "产品编号",
dataIndex: "goods_number",
},
{
title: "产品名称",
dataIndex: "goods_name",
},
{
title: "计划数量",
dataIndex: "total_quantity",
width: 100,
},
{
title: "完成数量",
dataIndex: "quantity_produced",
width: 100,
},
{
title: "计划开始时间",
dataIndex: "start_time",
width: 180,
},
{
title: "计划结束时间",
dataIndex: "end_time",
width: 180,
},
{
title: "操作",
dataIndex: "action",
fixed: "right",
width: 256,
scopedSlots: { customRender: "action" },
},
],
visible: false,
targetItem: {},
};
},
methods: {
initialize() {
this.list();
},
list() {
this.loading = true;
productionOrderList(this.searchForm)
.then((data) => {
this.pagination.total = data.count;
this.items = data.results;
})
.finally(() => {
this.loading = false;
});
},
create() {
this.list();
},
detial(item) {
this.$router.push({ path: "/production/detial", query: { id: item.id } });
},
search() {
this.searchForm.page = 1;
this.pagination.current = 1;
this.list();
},
tableChange(pagination, filters, sorter) {
this.searchForm.page = pagination.current;
this.pagination.current = pagination.current;
this.searchForm.ordering = `${sorter.order == "descend" ? "-" : ""}${sorter.field}`;
this.list();
},
onChangePicker(date, dateString) {
let startDate = date[0];
let endDate = date[1];
this.searchForm.start_date = startDate ? startDate.format("YYYY-MM-DD") : undefined;
this.searchForm.end_date = endDate ? endDate.add(1, "days").format("YYYY-MM-DD") : undefined;
this.search();
},
openCreateModal(item) {
this.targetItem = { ...item };
this.visible = true;
},
},
mounted() {
this.initialize();
},
};
</script>
<style scoped></style>

View file

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View file

@ -1,6 +0,0 @@
from django.apps import AppConfig
class ProductionConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.production'

View file

@ -1,26 +0,0 @@
from django_filters.rest_framework import FilterSet
from django_filters.filters import *
from apps.production.models import *
class ProductionOrderFilter(FilterSet):
start_date = DateFilter(field_name='create_time', lookup_expr='gte', label='开始日期')
end_date = DateFilter(field_name='create_time', lookup_expr='lt', label='结束日期')
class Meta:
model = ProductionOrder
fields = ['sales_order', 'goods', 'status', 'creator', 'start_date', 'end_date']
class ProductionRecordFilter(FilterSet):
start_date = DateFilter(field_name='create_time', lookup_expr='gte', label='开始日期')
end_date = DateFilter(field_name='create_time', lookup_expr='lt', label='结束日期')
class Meta:
model = ProductionRecord
fields = ['production_order', 'goods', 'creator', 'start_date', 'end_date']
__all__ = [
'ProductionOrderFilter', 'ProductionRecordFilter',
]

View file

@ -1,61 +0,0 @@
from extensions.common.base import *
from extensions.models import *
class ProductionOrder(Model):
"""生产单据"""
class Status(TextChoices):
IN_PLAN = ('in_plan', '计划中')
IN_PROGRESS = ('in_progress', '进行中')
COMPLETED = ('completed', '已完成')
CLOSED = ('closed', '强制关闭')
number = CharField(max_length=32, verbose_name='编号')
is_related = BooleanField(default=False, verbose_name='关联状态')
sales_order = ForeignKey(
'sales.SalesOrder', on_delete=PROTECT, null=True, related_name='production_orders', verbose_name='销售单')
goods = ForeignKey('goods.Goods', on_delete=PROTECT, related_name='production_orders', verbose_name='产品')
total_quantity = FloatField(verbose_name='生产总数')
quantity_produced = FloatField(verbose_name='已生产数量')
remain_quantity = FloatField(verbose_name='剩余数量')
stock_in_quantity = FloatField(default=0, verbose_name='入库数量')
start_time = DateTimeField(null=True, verbose_name='开始时间')
end_time = DateTimeField(null=True, verbose_name='结束时间')
status = CharField(max_length=32, choices=Status.choices, default=Status.IN_PLAN, verbose_name='状态')
creator = ForeignKey('system.User', on_delete=PROTECT,
related_name='created_production_orders', verbose_name='创建人')
create_time = DateTimeField(auto_now_add=True, verbose_name='创建时间')
team = ForeignKey('system.Team', on_delete=CASCADE, related_name='production_orders')
@classmethod
def get_number(cls, team):
start_date, end_date = pendulum.today().to_datetime_string(), pendulum.tomorrow().to_datetime_string()
instance = cls.objects.filter(team=team, create_time__gte=start_date, create_time__lt=end_date).last()
try:
result = re.match('^(.*?)([1-9]+)$', instance.number)
number = result.group(1) + str(int(result.group(2)) + 1)
except AttributeError:
number = 'SC' + pendulum.today().format('YYYYMMDD') + '0001'
return number
class ProductionRecord(Model):
"""生产记录"""
production_order = ForeignKey(
'production.ProductionOrder', on_delete=CASCADE, related_name='production_records', verbose_name='生产单')
goods = ForeignKey(
'goods.Goods', on_delete=PROTECT, related_name='production_records', verbose_name='产品')
production_quantity = FloatField(verbose_name='生产数量')
creator = ForeignKey(
'system.User', on_delete=PROTECT, related_name='created_production_records', verbose_name='创建人')
create_time = DateTimeField(auto_now_add=True, verbose_name='创建时间')
team = ForeignKey('system.Team', on_delete=CASCADE, related_name='production_records')
__all__ = [
'ProductionOrder', 'ProductionRecord',
]

View file

@ -1,14 +0,0 @@
from extensions.permissions import ModelPermission
class ProductionOrderPermission(ModelPermission):
code = 'production_order'
class ProductionRecordPermission(ModelPermission):
code = 'production_record'
__all__ = [
'ProductionOrderPermission', 'ProductionRecordPermission',
]

View file

@ -1,15 +0,0 @@
from extensions.serializers import *
class NumberResponse(Serializer):
number = CharField(label='编号')
class ProductionStockInRequest(Serializer):
warehouse = IntegerField(label='仓库')
stock_in_quantity = FloatField(label='入库数量')
__all__ = [
'NumberResponse', 'ProductionStockInRequest',
]

View file

@ -1,104 +0,0 @@
from extensions.common.base import *
from extensions.serializers import *
from extensions.exceptions import *
from apps.production.models import *
from apps.sales.models import *
from apps.goods.models import *
class ProductionOrderSerializer(BaseSerializer):
"""生产单据"""
class SalesGoodsItemSerializer(ModelSerializer):
goods_number = CharField(source='goods.number', read_only=True, label='产品编号')
goods_name = CharField(source='goods.name', read_only=True, label='产品名称')
goods_barcode = CharField(source='goods.barcode', read_only=True, label='产品条码')
unit_name = CharField(source='goods.unit.name', read_only=True, label='单位名称')
class Meta:
model = SalesGoods
fields = ['id', 'goods', 'goods_number', 'goods_name', 'goods_barcode', 'sales_quantity',
'sales_price', 'total_amount', 'return_quantity', 'unit_name']
sales_order_number = CharField(source='sales_order.number', read_only=True, label='销售单号')
sales_goods_items = SalesGoodsItemSerializer(
source='sales_order.sales_goods_set', many=True, read_only=True, label='销售产品Item')
goods_number = CharField(source='goods.number', read_only=True, label='产品编号')
goods_name = CharField(source='goods.name', read_only=True, label='产品名称')
status_display = CharField(source='get_status_display', read_only=True, label='状态')
creator_name = CharField(source='creator.name', read_only=True, label='创建人名称')
class Meta:
model = ProductionOrder
read_only_fields = ['id', 'sales_order_number', 'sales_goods_items', 'remain_quantity', 'stock_in_quantity',
'goods_number', 'goods_name', 'quantity_produced', 'remain_quantity', 'status', 'status_display',
'creator', 'creator_name', 'create_time']
fields = ['number', 'is_related', 'sales_order', 'goods', 'total_quantity',
'start_time', 'end_time', *read_only_fields]
def validate_number(self, value):
self.validate_unique({'number': value}, message=f'编号[{value}]已存在')
return value
def validate_sales_order(self, instance):
instance = self.validate_foreign_key(SalesOrder, instance, message='销售单不存在')
return instance
def validate_goods(self, instance):
instance = self.validate_foreign_key(Goods, instance, message='产品不存在')
return instance
def validate_total_quantity(self, value):
if value <= 0:
raise ValidationError('生产数量小于等于零')
return value
def validate(self, attrs):
if attrs['is_related'] and not attrs['sales_order']:
raise ValidationError('未关联销售单')
return super().validate(attrs)
def create(self, validated_data):
validated_data['quantity_produced'] = 0
validated_data['creator'] = self.user
return super().create(validated_data)
def save(self, **kwargs):
kwargs['remain_quantity'] = self.validated_data['total_quantity']
return super().save(**kwargs)
class ProductionRecordSerializer(BaseSerializer):
"""生产记录"""
production_order_number = CharField(source='production_order.number', read_only=True, label='生产单号')
goods_number = CharField(source='goods.number', read_only=True, label='产品编号')
goods_name = CharField(source='goods.name', read_only=True, label='产品名称')
creator_name = CharField(source='creator.name', read_only=True, label='创建人名称')
class Meta:
model = ProductionRecord
read_only_fields = ['id', 'production_order_number', 'goods', 'goods_number', 'goods_name', 'creator',
'creator_name', 'create_time']
fields = ['production_order', 'production_quantity', *read_only_fields]
def validate_production_order(self, instance):
instance = self.validate_foreign_key(ProductionOrder, instance, message='生产单不存在')
if instance.status != ProductionOrder.Status.IN_PROGRESS:
raise ValidationError(f'工单{instance.number}{instance.get_status_display()}, 无法创建')
return instance
def validate_production_quantity(self, value):
if value <= 0:
raise ValidationError('生产数量小于等于零')
return value
def create(self, validated_data):
validated_data['goods'] = validated_data['production_order'].goods
validated_data['creator'] = self.user
return super().create(validated_data)
__all__ = [
'ProductionOrderSerializer', 'ProductionRecordSerializer',
]

View file

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View file

@ -1,8 +0,0 @@
from extensions.routers import *
from apps.production.views import *
router = BaseRouter()
router.register('production_orders', ProductionOrderViewSet, 'production_order')
router.register('production_records', ProductionRecordViewSet, 'production_record')
urlpatterns = router.urls

View file

@ -1,145 +0,0 @@
from extensions.common.schema import *
from extensions.common.base import *
from extensions.permissions import *
from extensions.exceptions import *
from extensions.viewsets import *
from apps.production.serializers import *
from apps.production.permissions import *
from apps.production.filters import *
from apps.production.schemas import *
from apps.production.models import *
from apps.stock_in.models import *
from apps.data.models import *
class ProductionOrderViewSet(ModelViewSet):
"""生产单据"""
serializer_class = ProductionOrderSerializer
permission_classes = [IsAuthenticated, ProductionOrderPermission]
filterset_class = ProductionOrderFilter
search_fields = ['number', 'sales_order__number', 'goods__number', 'goods__name']
ordering_fields = ['number', 'total_quantity', 'quantity_produced', 'remain_quantity',
'start_time', 'end_time', 'create_time']
select_related_fields = ['sales_order', 'goods', 'creator']
prefetch_related_fields = ['sales_order__sales_goods_set']
queryset = ProductionOrder.objects.all()
def perform_update(self, serializer):
instance = serializer.instance
if instance.status != ProductionOrder.Status.IN_PLAN:
raise ValidationError(f'工单{instance.number}{instance.get_status_display()}, 无法编辑')
return super().perform_update(serializer)
def perform_destroy(self, instance):
if instance.status != ProductionOrder.Status.IN_PLAN:
raise ValidationError(f'工单{instance.number}{instance.get_status_display()}, 无法删除')
return super().perform_destroy(instance)
@extend_schema(responses={200: NumberResponse})
@action(detail=False, methods=['get'])
def number(self, request, *args, **kwargs):
"""获取编号"""
number = ProductionOrder.get_number(self.team)
return Response(data={'number': number}, status=status.HTTP_200_OK)
@extend_schema(responses={200: ProductionOrderSerializer})
@action(detail=True, methods=['post'])
def issue(self, request, *args, **kwargs):
"""发布工单"""
instance = self.get_object()
if instance.status != ProductionOrder.Status.IN_PLAN:
raise ValidationError(f'工单{instance.number}{instance.get_status_display()}, 无法发布工单')
instance.status = ProductionOrder.Status.IN_PROGRESS
instance.save(update_fields=['status'])
serializer = ProductionOrderSerializer(instance=instance)
return Response(data=serializer.data, status=status.HTTP_200_OK)
@extend_schema(responses={200: ProductionOrderSerializer})
@action(detail=True, methods=['post'])
def close(self, request, *args, **kwargs):
"""关闭工单"""
instance = self.get_object()
if instance.status != ProductionOrder.Status.IN_PROGRESS:
raise ValidationError(f'工单{instance.number}{instance.get_status_display()}, 无法关闭工单')
instance.status = ProductionOrder.Status.CLOSED
instance.save(update_fields=['status'])
serializer = ProductionOrderSerializer(instance=instance)
return Response(data=serializer.data, status=status.HTTP_200_OK)
@extend_schema(request=ProductionStockInRequest, responses={200: ProductionOrderSerializer})
@action(detail=True, methods=['post'])
def stock_in(self, request, *args, **kwargs):
"""入库"""
request_serializer = ProductionStockInRequest(data=request.data)
request_serializer.is_valid(raise_exception=True)
validated_data = request_serializer.validated_data
warehouse_id, stock_in_quantity = validated_data['warehouse'], validated_data['stock_in_quantity']
if not (warehouse := Warehouse.objects.filter(id=warehouse_id, team=self.team).first()):
raise ValidationError(f'仓库不存在')
production_order = self.get_object()
if production_order.status != ProductionOrder.Status.IN_PROGRESS:
raise ValidationError(f'工单{production_order.number}{production_order.get_status_display()}, 无法入库')
# 创建入库单据
stock_in_order_number = StockInOrder.get_number(team=self.team)
stock_in_order = StockInOrder.objects.create(
number=stock_in_order_number, warehouse=warehouse, type=StockInOrder.Type.PRODUCTION,
production_order=production_order, total_quantity=stock_in_quantity, remain_quantity=stock_in_quantity,
creator=self.user, team=self.team)
# 创建入库产品
StockInGoods.objects.create(
stock_in_order=stock_in_order, goods=production_order.goods, stock_in_quantity=stock_in_quantity,
remain_quantity=stock_in_quantity, team=self.team)
# 同步生产单据
production_order.stock_in_quantity = NP.plus(production_order.stock_in_quantity, stock_in_quantity)
production_order.save(update_fields=['stock_in_quantity'])
serializer = ProductionOrderSerializer(instance=production_order)
return Response(data=serializer.data, status=status.HTTP_200_OK)
class ProductionRecordViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, CreateModelMixin):
"""生产记录"""
serializer_class = ProductionRecordSerializer
permission_classes = [IsAuthenticated, ProductionRecordPermission]
filterset_class = ProductionRecordFilter
search_fields = ['production_order__number', 'goods__number', 'goods__name']
ordering_fields = ['production_quantity', 'create_time']
select_related_fields = ['production_order', 'goods', 'creator']
queryset = ProductionRecord.objects.all()
@transaction.atomic
def perform_create(self, serializer):
validated_data = serializer.validated_data
production_order = validated_data['production_order']
production_quantity = validated_data['production_quantity']
if production_order.remain_quantity < production_quantity:
raise ValidationError('生产数量错误')
production_order.quantity_produced = NP.plus(production_order.quantity_produced, production_quantity)
production_order.remain_quantity = NP.minus(production_order.remain_quantity, production_quantity)
if production_order.remain_quantity == 0:
production_order.status = ProductionOrder.Status.COMPLETED
production_order.save(update_fields=['quantity_produced', 'remain_quantity', 'status'])
serializer.save()
__all__ = [
'ProductionOrderViewSet', 'ProductionRecordViewSet',
]

View file

@ -11,7 +11,6 @@ class StockInOrder(Model):
PURCHASE = ('purchase', '采购') PURCHASE = ('purchase', '采购')
SALES_RETURN = ('sales_return', '销售退货') SALES_RETURN = ('sales_return', '销售退货')
STOCK_TRANSFER = ('stock_transfer', '调拨') STOCK_TRANSFER = ('stock_transfer', '调拨')
PRODUCTION = ('production', '生产')
number = CharField(max_length=32, verbose_name='编号') number = CharField(max_length=32, verbose_name='编号')
warehouse = ForeignKey('data.Warehouse', on_delete=PROTECT, warehouse = ForeignKey('data.Warehouse', on_delete=PROTECT,
@ -23,8 +22,6 @@ class StockInOrder(Model):
related_name='stock_in_order', verbose_name='销售退货单据') related_name='stock_in_order', verbose_name='销售退货单据')
stock_transfer_order = OneToOneField('stock_transfer.StockTransferOrder', on_delete=CASCADE, null=True, stock_transfer_order = OneToOneField('stock_transfer.StockTransferOrder', on_delete=CASCADE, null=True,
related_name='stock_in_order', verbose_name='调拨单据') related_name='stock_in_order', verbose_name='调拨单据')
production_order = OneToOneField('production.ProductionOrder', on_delete=CASCADE, null=True,
related_name='stock_in_order', verbose_name='生产单据')
total_quantity = FloatField(verbose_name='入库总数') total_quantity = FloatField(verbose_name='入库总数')
remain_quantity = FloatField(default=0, verbose_name='入库剩余数量') remain_quantity = FloatField(default=0, verbose_name='入库剩余数量')
is_completed = BooleanField(default=False, verbose_name='入库完成状态') is_completed = BooleanField(default=False, verbose_name='入库完成状态')

View file

@ -34,8 +34,6 @@ class StockInOrderSerializer(BaseSerializer):
source='sales_return_order.number', read_only=True, label='销售退货单据编号') source='sales_return_order.number', read_only=True, label='销售退货单据编号')
stock_transfer_order_number = CharField( stock_transfer_order_number = CharField(
source='stock_transfer_order.number', read_only=True, label='调拨单据编号') source='stock_transfer_order.number', read_only=True, label='调拨单据编号')
production_order_number = CharField(
source='production_order.number', read_only=True, label='生产单据编号')
creator_name = CharField(source='creator.name', read_only=True, label='创建人名称') creator_name = CharField(source='creator.name', read_only=True, label='创建人名称')
stock_in_goods_items = StockInGoodsItemSerializer( stock_in_goods_items = StockInGoodsItemSerializer(
source='stock_in_goods_set', many=True, label='入库产品Item') source='stock_in_goods_set', many=True, label='入库产品Item')
@ -45,7 +43,6 @@ class StockInOrderSerializer(BaseSerializer):
fields = ['id', 'number', 'warehouse', 'warehouse_number', 'warehouse_name', 'type', fields = ['id', 'number', 'warehouse', 'warehouse_number', 'warehouse_name', 'type',
'type_display', 'purchase_order', 'purchase_order_number', 'sales_return_order', 'type_display', 'purchase_order', 'purchase_order_number', 'sales_return_order',
'sales_return_order_number', 'stock_transfer_order', 'stock_transfer_order_number', 'sales_return_order_number', 'stock_transfer_order', 'stock_transfer_order_number',
'production_order', 'production_order_number',
'total_quantity', 'remain_quantity', 'is_completed', 'is_void', 'creator', 'total_quantity', 'remain_quantity', 'is_completed', 'is_void', 'creator',
'creator_name', 'create_time', 'stock_in_goods_items'] 'creator_name', 'create_time', 'stock_in_goods_items']

26
erp_mobile/.gitignore vendored
View file

@ -1,26 +0,0 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
unpackage/*
!unpackage/res

View file

@ -1,16 +0,0 @@
{ // launch.json configurations app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtypelocalremote, localremote
"version": "0.0",
"configurations": [{
"app-plus" :
{
"launchtype" : "local"
},
"default" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

View file

@ -1,39 +0,0 @@
<style lang="scss">
/* 注意要写在第一行同时给style标签加入lang="scss"属性 */
@import "@/uni_modules/uview-ui/index.scss";
</style>
<script>
var globalEvent = uni.requireNativePlugin('globalEvent');
export default {
onLaunch: function() {
console.log('App Launch')
globalEvent.addEventListener('onPrintCallback', function(e) {
console.log(e.key)
if (e.key == 0) {
uni.showToast({
title: '打印成功',
duration: 2000
});
} else if (e.key == 3) {
uni.showToast({
title: '缺纸',
duration: 2000
});
}
});
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>

View file

@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -1,18 +0,0 @@
# 海鸥云ERP_PDA移动端
### 介绍
海鸥云ERP移动端采用前后端分离技术api使用restful协议方便二次开发后端使用PythonDjangoDRF等技术移动端使用uniapp进行构建主要包含仓库操作人员的扫码操作流程
### 软件说明
该软件为海鸥云ERP的PDA端代码PC及后端代码请参考仓库
* Gitee地址: [Gitee](https://gitee.com/haioucloud/erp)
* Demo地址: [Demo](http://114.218.158.78:12222/) &nbsp;&nbsp;公司编号: admin 测试帐号admin 密码admin
### 使用前须知
* 软件开放源码(发行协议:GPL-3.0),用户可免费使用,但禁止任何单位或个人修改软件后再次发行的行为。商业使用需得到我司商业授权。
* 我们欢迎对开源技术感兴趣的朋友一起加入到我们项目中来完善系统功能并为客户提供服务。欢迎扫描下方二维码添加技术交流群,添加时请备注来意
![微信群](https://gitee.com/haioucloud/erp/raw/master/raw/%E5%BE%AE%E4%BF%A1%E7%BE%A4.png)
* 功能定制或代理授权咨询Tel:18761717855或扫描下方二维码联系
![微信](https://gitee.com/haioucloud/erp/raw/master/raw/%E5%BE%AE%E4%BF%A1.png)

View file

@ -1,31 +0,0 @@
import request from "@/utils/request.js";
// UserOption
export function userOption(data) {
return request({ url: `/users/options/`, method: 'get', data })
}
// Warehouse
export function warehouseOption(data) {
return request({ url: `/warehouses/options/`, method: 'get', data })
}
// BatchOption
export function batchOption(data) {
return request({ url: `/batchs/options/`, method: 'get', data })
}
// InventoryOption
export function inventoryOption(data) {
return request({ url: `/inventories/options/`, method: 'get', data })
}
// Category
export function categoryOption(data) {
return request({ url: `/goods_categories/options/`, method: 'get', data })
}
// Unit
export function unitOption(data) {
return request({ url: `/goods_units/options/`, method: 'get', data })
}

View file

@ -1,23 +0,0 @@
import request from "@/utils/request.js";
// Product
export function productList(data) {
return request({ url: '/goods/', method: 'get', data });
}
export function productCreate(data) {
return request({ url: '/goods/', method: 'post', data });
}
export function productUpdate(data) {
return request({ url: `/goods/${data.id}/`, method: 'put', data });
}
export function productDestroy(data) {
return request({ url: `/goods/${data.id}/`, method: 'delete', data });
}
export function productNumber(data) {
return request({ url: '/goods/number/', method: 'get', data });
}

View file

@ -1,6 +0,0 @@
import request from "@/utils/request.js";
// 批次报表
export function batchReportList(data) {
return request({ url: `/batchs/`, method: 'get', data });
}

View file

@ -1,10 +0,0 @@
import request from "@/utils/request.js";
export function stockCheckNumber(params) {
return request({ url: `/stock_check_orders/number/`, method: 'get', params })
}
export function stockCheckCreate(data) {
return request({ url: `/stock_check_orders/`, method: 'post', data })
}

View file

@ -1,11 +0,0 @@
import request from "@/utils/request.js";
// 入库任务
export function stockInTaskList(data) {
return request({ url: `/stock_in_orders/`, method: 'get', data });
}
// 入库记录
export function stockInRecordCreate(data) {
return request({ url: `/stock_in_records/`, method: 'post', data });
}

View file

@ -1,11 +0,0 @@
import request from "@/utils/request.js";
// 出库任务
export function stockOutTaskList(data) {
return request({ url: `/stock_out_orders/`, method: 'get', data });
}
// 出库记录
export function stockOutRecordCreate(data) {
return request({ url: `/stock_out_records/`, method: 'post', data });
}

View file

@ -1,22 +0,0 @@
import request from "@/utils/request.js";
// 获取令牌
export function getToken(data) {
return request({ url: '/user/get_token/', method: 'post', header: {}, data });
}
// 获取用户信息
export function getInfo(data) {
return request({ url: '/user/info/', method: 'get', data });
}
// 刷新令牌
export function refreshToken(data) {
return request({ url: '/user/refresh_token/', method: 'post', header: {}, data });
}
// 首页概览
export function homeOverview(data) {
return request({ url: `/home_overview/`, method: "get", data });
}

View file

@ -1,75 +0,0 @@
<template>
<u-modal :show="show" title="打印次数" :showCancelButton="true" confirmText="打印" @cancel="cancel" @confirm="confirm">
<view class="slot-content">
<view>
<u--input type="digit" border="surround" :focus="true" v-model="printTimes"></u--input>
</view>
</view>
</u-modal>
</template>
<script>
const printer = uni.requireNativePlugin('LcPrinter');
export default {
props: ["show", "number", "name"],
data() {
return {
printTimes: 1,
}
},
methods: {
initPrinter() {
printer.initPrinter({});
printer.printEnableMark({ enable: true });
printer.setFontSize({ fontSize: 8 });
},
confirm() {
if (printer === undefined) {
uni.showToast({
title: '打印失败请联系软件厂商购买指定型号PDA',
duration: 2000
});
}
for (let index = 0; index < this.printTimes; index++) {
printer.printLine({ line_length: 1 })
printer.printBarcode2({
offset: 1,
text: this.number,
height: 150,
barcodeType: 73,
hriPosition: 1,
});
printer.printLine({ line_length: 1 })
printer.printText2({
offset: 2,
fontSize: 3,
isBold: false,
isUnderLine: false,
content: `编号: ${this.number}\n`
});
printer.printText2({
offset: 2,
fontSize: 3,
isBold: false,
isUnderLine: false,
content: `名称: ${this.name}`
});
printer.printGoToNextMark();
}
this.cancel();
},
cancel() {
this.printTimes = 1;
this.$emit("cancel", false);
},
},
mounted() {
this.initPrinter();
},
}
</script>
<style>
</style>

View file

@ -1,20 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

View file

@ -1,30 +0,0 @@
import App from './App';
import store from 'store';
// #ifndef VUE3
import Vue from 'vue'
import uView from '@/uni_modules/uview-ui'
Vue.use(uView)
Vue.config.productionTip = false
Vue.prototype.$store = store;
App.mpType = 'app'
const app = new Vue({
store,
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif

View file

@ -1,122 +0,0 @@
{
"name" : "海鸥云ERP",
"appid" : "__UNI__EF5FEA2",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {
"ad" : {}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
}
},
"nativePlugins" : {
"LcPrinter" : {
"__plugin_info__" : {
"name" : "LcPrinter",
"description" : "打印插件",
"platforms" : "Android",
"url" : "",
"android_package_name" : "",
"ios_bundle_id" : "",
"isCloud" : false,
"bought" : -1,
"pid" : "",
"parameters" : {}
}
}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
}

View file

@ -1,25 +0,0 @@
{
"name": "LcPrinter",
"id": "LcPrinter",
"version": "1.0.1",
"description": "打印插件",
"_dp_type":"nativeplugin",
"_dp_nativeplugin":{
"android": {
"plugins": [
{
"type": "module",
"name": "LcPrinter",
"class": "uni.dcloud.io.uniplugin_lcprint.PrinterModule"
}
],
"abis": [
"armeabi-v7a",
"arm64-v8a",
"x86"
],
"integrateType": "aar",
"minSdkVersion" : 15
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +0,0 @@
{
"dependencies": {
"node-sass": "^7.0.1",
"sass-loader": "^12.6.0",
"vuex": "^4.0.2"
}
}

View file

@ -1,88 +0,0 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/home/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": false
}
},
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path": "pages/productList/index",
"style": {
"navigationBarTitleText": "产品管理",
"enablePullDownRefresh": true
}
},
{
"path": "pages/productCreateForm/index",
"style": {
"navigationBarTitleText": "新增产品",
"enablePullDownRefresh": false
}
},
{
"path": "pages/productUpdateForm/index",
"style": {
"navigationBarTitleText": "编辑产品",
"enablePullDownRefresh": false
}
},
{
"path": "pages/batchReport/index",
"style": {
"navigationBarTitleText": "批次报表",
"enablePullDownRefresh": true
}
},
{
"path": "pages/stockInTask/index",
"style": {
"navigationBarTitleText": "入库任务",
"enablePullDownRefresh": false
}
},
{
"path": "pages/stockInForm/index",
"style": {
"navigationBarTitleText": "入库商品",
"enablePullDownRefresh": false
}
},
{
"path": "pages/stockOutTask/index",
"style": {
"navigationBarTitleText": "出库任务",
"enablePullDownRefresh": false
}
},
{
"path": "pages/stockOutForm/index",
"style": {
"navigationBarTitleText": "出库商品",
"enablePullDownRefresh": false
}
},
{
"path": "pages/stockCheckForm/index",
"style": {
"navigationBarTitleText": "盘点",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#3c9cff",
"backgroundColor": "#F8F8F8"
}
}

View file

@ -1,123 +0,0 @@
<template>
<view>
<view style="padding: 12rpx;">
<u-search placeholder="请扫描商品条码" :focus="true" searchIcon="scan" v-model="searchForm.search" shape="square"
:clearabled="true" :show-action="false" @search="searchData">
</u-search>
</view>
<view>
<u-cell v-for="(item, index) in dataItems" :key="index">
<view class="cell-title" slot="title">{{ item.goods_number }}</view>
<view class="cell-label" slot="label">
<u-row class="cell-label-row">
<u-col span="6">商品名称: {{ item.goods_name }}</u-col>
<u-col span="6">批次: {{ item.number }}</u-col>
</u-row>
<u-row class="cell-label-row">
<u-col span="6">仓库: {{ item.warehouse_name }}</u-col>
<u-col span="6">数量: {{ item.remain_quantity }}</u-col>
</u-row>
</view>
</u-cell>
</view>
<view style="padding: 12rpx 0;">
<u-loadmore :status="loadMoreStatus" loading-text="正在加载中" loadmore-text="加载更多" nomore-text="没有更多了"
@loadmore="loadMoreData" />
</view>
</view>
</template>
<script>
import { batchReportList } from '@/api/report.js';
import { mapState } from 'vuex';
export default {
data() {
return {
searchForm: { page: 1, page_size: 6, has_stock: true },
dataLoading: false,
dataItems: [],
dataCount: 0,
}
},
computed: {
...mapState({
currentWarehouse: (state) => state.system.currentWarehouse,
}),
loadMoreStatus() {
if (this.dataLoading) {
return 'loading';
}
if (this.dataItems.length == this.dataCount) {
return 'nomore';
}
return 'loadmore';
},
},
methods: {
initData() {
this.searchForm.warehouse = this.currentWarehouse;
this.searchForm.page = 1;
this.list();
},
list() {
if (this.searchForm.page == 1) {
uni.showLoading({ title: '加载中' });
}
this.dataLoading = true;
batchReportList(this.searchForm).then((data) => {
console.log('-----------------------BatchReportStart-----------------------')
this.dataCount = data.count;
if (this.searchForm.page == 1) {
this.dataItems = data.results;
} else {
this.dataItems = this.dataItems.concat(data.results);
}
}).finally(() => {
console.log('-----------------------BatchReportEnd-----------------------')
uni.stopPullDownRefresh();
uni.hideLoading();
this.dataLoading = false;
});
},
loadMoreData() {
if (this.dataItems.length < this.dataCount) {
this.searchForm.page += 1;
this.list();
}
},
searchData() {
this.searchForm.page = 1;
this.list();
},
},
onShow() {
this.initData();
},
onPullDownRefresh() {
this.searchData();
},
}
</script>
<style scoped>
.cell-title {
font-size: 32rpx;
font-weight: 500;
margin-bottom: 12rpx;
}
.cell-label {
font-size: 24rpx;
color: #0005;
}
.cell-label-row {
margin-bottom: 8rpx;
}
</style>

View file

@ -1,159 +0,0 @@
<template>
<view style="background-color: #f8f8f8; height: 100vh;">
<view>
<u-row style="background-color: #3c9cff; color: #ffffff; height: 72rpx; padding-right: 16rpx;">
<u-col span="3" @click="showPicker = true" style="text-align: left; float: left;">
<u-icon name="arrow-down" :label="currentWarehouseName" labelPos="left" color="#ffffff" labelColor="#ffffff"
space="6px" style="font-weight: 600; text-align: left; float: left;"></u-icon>
</u-col>
<u-col offset="6" span="3" @click="showActionSheet = true">
<u-icon name="arrow-down" :label="userItem.name" labelPos="left" color="#ffffff" labelColor="#ffffff"
space="6px" style="font-weight: 600;"></u-icon>
</u-col>
</u-row>
<u-action-sheet :actions="actionList" :show="showActionSheet" :closeOnClickOverlay="true"
@close="showActionSheet = false" @select="selectAction"></u-action-sheet>
<u-picker :show="showPicker" :columns="[warehouseItems]" keyName="name" :closeOnClickOverlay="true"
@close="showPicker = false" @cancel="showPicker = false" @confirm="changeCurrentWarehouse"></u-picker>
</view>
<u-row style="background-color: #fff;">
<u-col span="4">
<view
style="color: #666; text-align: center; border-bottom: 1px solid #8888; border-right: 1px solid #8888; padding: 20rpx 0;">
<view>待入库</view>
<view style="color: #398ade;">{{ item.stock_in_task_count }}</view>
</view>
</u-col>
<u-col span="4">
<view style="color: #666; text-align: center; border-bottom: 1px solid #8888; padding: 20rpx 0;">
<view>待出库</view>
<view style="color: #53c21d;">{{ item.stock_out_task_count }}</view>
</view>
</u-col>
<u-col span="4">
<view
style="color: #666; text-align: center; border-bottom: 1px solid #8888; border-left: 1px solid #8888;padding: 20rpx 0;">
<view>临期预警</view>
<view style="color: #e45656;">{{ item.expiration_warning_count }}</view>
</view>
</u-col>
</u-row>
<u-grid :border="false" @click="onClickGrid">
<u-grid-item v-for="(item, index) in funcList" :key="index"
style="padding: 36rpx 0; border-right:1px solid #8888; border-bottom: 1px solid #8888;">
<image :src="item.image" style="width: 100rpx; height: 100rpx" />
<view style="margin-top: 16rpx;">
<text style="font-size: 28rpx;">{{item.title}}</text>
</view>
</u-grid-item>
</u-grid>
</view>
</template>
<script>
import { getInfo, homeOverview } from 'api/system.js';
import { warehouseOption } from 'api/option.js';
import { mapState, mapMutations } from 'vuex';
export default {
data() {
return {
funcList: [
{
title: '产品管理',
path: '/pages/productList/index',
image: '../../static/product.png',
},
{
title: '批次报表',
path: '/pages/batchReport/index',
image: '../../static/func6.png',
},
{
title: '入库',
path: '/pages/stockInTask/index',
image: '../../static/func1.png',
},
{
title: '出库',
path: '/pages/stockOutTask/index',
image: '../../static/func8.png',
},
{
title: '盘点',
path: '/pages/stockCheckForm/index',
image: '../../static/func9.png',
},
],
showActionSheet: false,
showPicker: false,
actionList: [
{ code: 'logout', name: '退出登录' }
],
item: {},
}
},
computed: {
...mapState({
userItem: (state) => state.system.userItem,
warehouseItems: (state) => state.system.warehouseItems,
currentWarehouse: (state) => state.system.currentWarehouse,
}),
currentWarehouseName() {
for (let item of this.warehouseItems) {
if (item.id == this.currentWarehouse) {
return item.name;
}
}
return '所有仓库';
},
},
methods: {
...mapMutations(['setUserItem', 'setWarehouseItems', 'setCurrentWarehouse']),
initData() {
getInfo().then((data) => {
console.log(data)
this.setUserItem(data);
});
warehouseOption().then((data) => {
let warehouseItems = [{ id: undefined, name: '所有仓库' }, ...data.results];
this.setWarehouseItems(warehouseItems);
if (!this.currentWarehouse) {
this.setCurrentWarehouse(warehouseItems[0].id);
}
});
homeOverview().then((data) => {
this.item = data;
});
},
onClickGrid(index) {
uni.navigateTo({ url: this.funcList[index].path });
},
changeCurrentWarehouse(item) {
this.showPicker = false;
this.setCurrentWarehouse(this.warehouseItems[item.indexs[0]].id);
},
selectAction(item) {
this.showActionSheet = false;
if (item.code == 'logout') {
uni.removeStorageSync('access');
uni.removeStorageSync('refresh');
uni.redirectTo({ url: '../login/index' });
}
},
},
onShow() {
this.initData();
},
}
</script>
<style>
</style>

View file

@ -1,98 +0,0 @@
<template>
<view style="text-align: center;">
<!-- <view style="padding-top: 180rpx;">
<image src="../../static/images/logo.png" style="width: 180rpx; height: 180rpx;"></image>
</view>
-->
<view style="padding-top: 180rpx;">
<image src="../../static/images/logo.png" style="width: 160rpx; height: 160rpx;"></image>
</view>
<view style="color: rgb(24, 144, 255); margin-top: 24rpx;">
<view style="font-size: 52rpx; font-weight: 800;">Himool ERP</view>
<view style="font-size: 30rpx; margin-top: 8rpx;">盒木进销存管理系统</view>
</view>
<view style="width: 70%; margin: auto; padding-top: 10rpx;">
<view class="form-input">
<u--input v-model="form.number" placeholder="公司编号" border="none" prefixIcon="tags"
prefixIconStyle="font-size: 24px; color: #3c9cffbb" fontSize="18px" clearable></u--input>
</view>
<view class="form-input">
<u--input v-model="form.username" placeholder="用户名" border="none" prefixIcon="account"
prefixIconStyle="font-size: 24px; color: #3c9cffbb" fontSize="18px" clearable></u--input>
</view>
<view class="form-input">
<u--input v-model="form.password" placeholder="密码" border="none" prefixIcon="lock"
prefixIconStyle="font-size: 24px; color: #3c9cffbb" fontSize="18px" clearable type="password"></u--input>
</view>
<view style="padding-top: 80rpx;">
<u-button type="primary" text="登录" size="large" :loading="loginLoading" :disabled="loginLoading"
@click="handleLogin"></u-button>
</view>
</view>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import { getToken } from '@/api/system.js';
export default {
data() {
return {
loginLoading: false,
form: {
number: '',
username: '',
password: '',
},
};
},
methods: {
handleLogin() {
if (!this.validateForm()) {
return;
}
this.loginLoading = true;
getToken(this.form).then((data) => {
uni.setStorageSync('access', data.access);
uni.setStorageSync('refresh', data.refresh);
uni.redirectTo({ url: '../home/index' });
}).finally(() => {
this.loginLoading = false;
})
},
validateForm() {
if (!this.form.number) {
this.$refs.uToast.show({ type: 'warning', duration: 1000, message: '请输入公司编号' })
return false;
}
if (!this.form.username) {
this.$refs.uToast.show({ type: 'warning', duration: 1000, message: '请输入用户名' })
return false;
}
if (!this.form.password) {
this.$refs.uToast.show({ type: 'warning', duration: 1000, message: '请输入密码' })
return false;
}
return true;
},
},
}
</script>
<style scoped>
.form-input {
border-bottom: 1px solid #3c9cffbb;
padding: 16rpx 8rpx;
margin-top: 16rpx;
}
</style>

View file

@ -1,174 +0,0 @@
<template>
<view>
<view style="padding: 0 24rpx; margin-bottom: 100rpx;">
<u--form labelWidth="100">
<u-form-item label="编号:" required borderBottom>
<u--input v-model="dataForm.number" border="none" />
</u-form-item>
<u-form-item label="名称:" required borderBottom>
<u--input v-model="dataForm.name" border="none" />
</u-form-item>
<u-form-item label="条码:" borderBottom>
<u--input v-model="dataForm.barcode" border="none" />
</u-form-item>
</u-form-item>
<u-form-item label="分类:" borderBottom @click="showCategoryPicker = true">
<u--input :value="dataForm.category_name" disabled disabledColor="#ffffff" border="none" />
</u-form-item>
<u-form-item label="单位:" borderBottom @click="showUnitPicker = true">
<u--input :value="dataForm.unit_name" disabled disabledColor="#ffffff" border="none" />
</u-form-item>
<u-form-item label="规格:" borderBottom>
<u--input v-model="dataForm.spec" border="none" />
</u-form-item>
<u-form-item label="采购价:" borderBottom>
<u--input v-model="dataForm.purchase_price" type="number" border="none" />
</u-form-item>
<u-form-item label="零售价:" borderBottom>
<u--input v-model="dataForm.retail_price" type="number" border="none" />
</u-form-item>
<u-form-item label="等级价一:" borderBottom>
<u--input v-model="dataForm.level_price1" type="number" border="none" />
</u-form-item>
<u-form-item label="等级价二:" borderBottom>
<u--input v-model="dataForm.level_price2" type="number" border="none" />
</u-form-item>
<u-form-item label="等级价三:" borderBottom>
<u--input v-model="dataForm.level_price3" type="number" border="none" />
</u-form-item>
<u-form-item label="启用批次控制:" borderBottom>
<u-switch v-model="dataForm.enable_batch_control" />
</u-form-item>
<u-form-item v-if="dataForm.enable_batch_control" label="保质期(天):" borderBottom>
<u--input v-model="dataForm.shelf_life_days" border="none" />
</u-form-item>
<u-form-item v-if="dataForm.enable_batch_control" label="预警天数:" borderBottom>
<u--input v-model="dataForm.shelf_life_warning_days" border="none" />
</u-form-item>
<u-form-item label="库存预警:" borderBottom>
<u-switch v-model="dataForm.enable_inventory_warning" />
</u-form-item>
<u-form-item v-if="dataForm.enable_inventory_warning" label="库存上限:" borderBottom>
<u--input v-model="dataForm.inventory_upper" border="none" />
</u-form-item>
<u-form-item v-if="dataForm.enable_inventory_warning" label="库存下限:" borderBottom>
<u--input v-model="dataForm.inventory_lower" border="none" />
</u-form-item>
<u-form-item label="状态:" borderBottom>
<u-switch v-model="dataForm.is_active" />
</u-form-item>
<u-form-item label="备注:" borderBottom>
<u--input v-model="dataForm.remark" border="none" />
</u-form-item>
</u--form>
</view>
<view style="position: fixed; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="创建" @click="create"></u-button>
</view>
<u-toast ref="uToast"></u-toast>
<u-picker :show="showCategoryPicker" :columns="[categoryItems]" keyName="name" :closeOnClickOverlay="true"
@close="showCategoryPicker = false" @cancel="showCategoryPicker = false" @confirm="changeCategory" />
<u-picker :show="showUnitPicker" :columns="[unitItems]" keyName="name" :closeOnClickOverlay="true"
@close="showUnitPicker = false" @cancel="showUnitPicker = false" @confirm="changeUnit" />
</view>
</template>
<script>
import { productCreate, productNumber } from '@/api/product.js';
import { categoryOption, unitOption } from '@/api/option.js';
export default {
data() {
return {
dataForm: {
number: '',
name: '',
barcode: '',
unit: null,
unit_name: '',
category: null,
category_name: '',
spec: '',
length: null,
width: null,
height: null,
volume: null,
weight: null,
spec: '',
unit: '',
enable_shelf_life: false,
shelf_life_days: null,
shelf_life_warning_days: 0,
enable_inventory_warning: false,
inventory_upper: null,
inventory_lower: null,
is_active: true,
remark: '',
},
showUnitPicker: false,
showCategoryPicker: false,
unitItems: [],
categoryItems: [],
}
},
methods: {
create() {
if (!this.dataForm.number) {
this.$refs.uToast.show({ message: '请填写编号' });
return
}
if (!this.dataForm.name) {
this.$refs.uToast.show({ message: '请填写名称' });
return
}
productCreate(this.dataForm).then((data) => {
uni.showToast({ title: '创建成功', duration: 2000 });
uni.navigateBack();
this.eventChannel.emit('onCreate', data);
});
},
changeUnit(item) {
this.showUnitPicker = false;
this.dataForm.unit = this.unitItems[item.indexs[0]].id;
this.dataForm.unit_name = this.unitItems[item.indexs[0]].name;
},
changeCategory(item) {
this.showCategoryPicker = false;
this.dataForm.category = this.categoryItems[item.indexs[0]].id;
this.dataForm.category_name = this.categoryItems[item.indexs[0]].name;
},
},
onShow() {
if (!this.dataForm.number || this.dataForm.number.length === 0) {
productNumber().then((data) => {
this.dataForm.number = data.number;
});
}
unitOption({ page: 1, page_size: 999999 }).then((data) => {
this.unitItems = data.results;
});
categoryOption({ page: 1, page_size: 999999 }).then((data) => {
this.categoryItems = data.results;
});
},
onLoad(option) {
this.eventChannel = this.getOpenerEventChannel();
if (option.number) {
this.dataForm.number = option.number;
}
},
}
</script>
<style scoped>
</style>

View file

@ -1,190 +0,0 @@
<template>
<view style="margin-bottom: 100rpx;">
<view style="padding: 12rpx;">
<u-search placeholder="请扫描物料条码" :focus="true" searchIcon="scan" v-model="searchForm.search" shape="square"
:clearabled="true" :show-action="false" @search="searchData">
</u-search>
</view>
<view>
<u-cell v-for="(item, index) in dataItems" :key="index">
<view class="cell-title" slot="title">{{ item.name }}</view>
<view class="cell-label" slot="label">
<u-row class="cell-label-row">
<u-col span="6">编号: {{ item.number }}</u-col>
<u-col span="6">分类: {{ item.category_name }}</u-col>
</u-row>
<u-row class="cell-label-row">
<u-col span="6">规格: {{ item.spec }}</u-col>
<u-col span="6">备注: {{ item.remark }}</u-col>
</u-row>
<u-row gutter="16" style="margin-top: 24rpx;">
<u-col span="4">
<u-button type="primary" text="编辑" @click="toUpdateForm(item)"></u-button>
</u-col>
<u-col span="4">
<u-button type="error" text="删除" @click="openDestroyModal(item)"></u-button>
</u-col>
<u-col span="4">
<u-button type="success" text="打印" @click="openPrintModal(item)"></u-button>
</u-col>
</u-row>
</view>
</u-cell>
</view>
<view style="position: fixed; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="新增物料" @click="toCreateForm">
</u-button>
</view>
<view style="padding: 12rpx 0;">
<u-loadmore :status="loadMoreStatus" loading-text="正在加载中" loadmore-text="加载更多" nomore-text="没有更多了"
@loadmore="loadMoreData" />
</view>
<u-modal :show="showDestroyModal" title="确定删除吗?" :showCancelButton="true" @cancel="showDestroyModal = false"
@confirm="destroy" />
<print-material-modal :show="showPrintModal" :number="printItem.number" :name="printItem.name"
@cancel="showPrintModal = false" />
</view>
</template>
<script>
import PrintMaterialModal from '@/components/PrintMaterialModal/index';
import { productList, productDestroy } from '@/api/product.js';
import { mapState } from 'vuex';
export default {
components: {
PrintMaterialModal,
},
data() {
return {
searchForm: { page: 1, page_size: 8 },
dataLoading: false,
dataItems: [],
dataCount: 0,
showPrintModal: false,
printItem: {},
showDestroyModal: false,
destroyItem: {},
}
},
computed: {
...mapState({
currentWarehouse: (state) => state.system.currentWarehouse,
}),
loadMoreStatus() {
if (this.dataLoading) {
return 'loading';
}
if (this.dataItems.length == this.dataCount) {
return 'nomore';
}
return 'loadmore';
},
},
methods: {
initData() {
this.searchForm.page = 1;
this.list();
},
list() {
if (this.searchForm.page == 1) {
uni.showLoading({ title: '加载中' });
}
this.dataLoading = true;
productList(this.searchForm).then((data) => {
this.dataCount = data.count;
if (this.searchForm.page == 1) {
this.dataItems = data.results;
} else {
this.dataItems = this.dataItems.concat(data.results);
}
}).finally(() => {
uni.stopPullDownRefresh();
uni.hideLoading();
this.dataLoading = false;
});
},
destroy() {
productDestroy({ id: this.destroyItem.id }).then(() => {
this.dataItems.splice(this.dataItems.findIndex(item => item.id == this.destroyItem.id), 1);
uni.showToast({ title: '删除成功', duration: 2000 });
this.showDestroyModal = false;
});
},
loadMoreData() {
if (this.dataItems.length < this.dataCount) {
this.searchForm.page += 1;
this.list();
}
},
searchData() {
this.searchForm.page = 1;
this.list();
},
openDestroyModal(item) {
this.destroyItem = { ...item };
this.showDestroyModal = true;
},
openPrintModal(item) {
this.printItem = item;
this.showPrintModal = true;
},
toCreateForm() {
uni.navigateTo({
url: '../productCreateForm/index',
events: {
onCreate(item) {
this.dataItems.splice(0, 0, item);
},
},
});
},
toUpdateForm(item) {
uni.navigateTo({
url: '../productUpdateForm/index?item=' + encodeURIComponent(JSON.stringify(item)),
events: {
onUpdate(item) {
let index = this.dataItems.findIndex((dataItem) => dataItem.id === item.id);
if (index !== -1) {
this.dataItems.splice(index, 1, item);
}
},
},
});
},
},
onShow() {
this.initData();
},
onPullDownRefresh() {
this.searchData();
},
}
</script>
<style scoped>
.cell-title {
font-size: 32rpx;
font-weight: 500;
margin-bottom: 12rpx;
}
.cell-label {
font-size: 24rpx;
color: #0005;
}
.cell-label-row {
margin-bottom: 8rpx;
}
</style>

View file

@ -1,166 +0,0 @@
<template>
<view>
<view style="padding: 0 24rpx; margin-bottom: 100rpx;">
<u--form labelWidth="100">
<u-form-item label="编号:" required borderBottom>
<u--input v-model="dataForm.number" border="none" />
</u-form-item>
<u-form-item label="名称:" required borderBottom>
<u--input v-model="dataForm.name" border="none" />
</u-form-item>
<u-form-item label="条码:" borderBottom>
<u--input v-model="dataForm.barcode" border="none" />
</u-form-item>
</u-form-item>
<u-form-item label="分类:" borderBottom @click="showCategoryPicker = true">
<u--input :value="dataForm.category_name" disabled disabledColor="#ffffff" border="none" />
</u-form-item>
<u-form-item label="单位:" borderBottom @click="showUnitPicker = true">
<u--input :value="dataForm.unit_name" disabled disabledColor="#ffffff" border="none" />
</u-form-item>
<u-form-item label="规格:" borderBottom>
<u--input v-model="dataForm.spec" border="none" />
</u-form-item>
<u-form-item label="采购价:" borderBottom>
<u--input v-model="dataForm.purchase_price" type="number" border="none" />
</u-form-item>
<u-form-item label="零售价:" borderBottom>
<u--input v-model="dataForm.retail_price" type="number" border="none" />
</u-form-item>
<u-form-item label="等级价一:" borderBottom>
<u--input v-model="dataForm.level_price1" type="number" border="none" />
</u-form-item>
<u-form-item label="等级价二:" borderBottom>
<u--input v-model="dataForm.level_price2" type="number" border="none" />
</u-form-item>
<u-form-item label="等级价三:" borderBottom>
<u--input v-model="dataForm.level_price3" type="number" border="none" />
</u-form-item>
<u-form-item label="启用批次控制:" borderBottom>
<u-switch v-model="dataForm.enable_batch_control" />
</u-form-item>
<u-form-item v-if="dataForm.enable_batch_control" label="保质期(天):" borderBottom>
<u--input v-model="dataForm.shelf_life_days" border="none" />
</u-form-item>
<u-form-item v-if="dataForm.enable_batch_control" label="预警天数:" borderBottom>
<u--input v-model="dataForm.shelf_life_warning_days" border="none" />
</u-form-item>
<u-form-item label="库存预警:" borderBottom>
<u-switch v-model="dataForm.enable_inventory_warning" />
</u-form-item>
<u-form-item v-if="dataForm.enable_inventory_warning" label="库存上限:" borderBottom>
<u--input v-model="dataForm.inventory_upper" border="none" />
</u-form-item>
<u-form-item v-if="dataForm.enable_inventory_warning" label="库存下限:" borderBottom>
<u--input v-model="dataForm.inventory_lower" border="none" />
</u-form-item>
<u-form-item label="状态:" borderBottom>
<u-switch v-model="dataForm.is_active" />
</u-form-item>
<u-form-item label="备注:" borderBottom>
<u--input v-model="dataForm.remark" border="none" />
</u-form-item>
</u--form>
</view>
<view style="position: fixed; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="保存" @click="update"></u-button>
</view>
<u-toast ref="uToast"></u-toast>
<u-picker :show="showCategoryPicker" :columns="[categoryItems]" keyName="name" :closeOnClickOverlay="true"
@close="showCategoryPicker = false" @cancel="showCategoryPicker = false" @confirm="changeCategory" />
<u-picker :show="showUnitPicker" :columns="[unitItems]" keyName="name" :closeOnClickOverlay="true"
@close="showUnitPicker = false" @cancel="showUnitPicker = false" @confirm="changeUnit" />
</view>
</template>
<script>
import { productUpdate } from '@/api/product.js';
import { categoryOption, unitOption } from '@/api/option.js';
export default {
data() {
return {
dataForm: {
number: '',
name: '',
barcode: '',
unit: null,
unit_name: '',
category: null,
category_name: '',
spec: '',
length: null,
width: null,
height: null,
volume: null,
weight: null,
spec: '',
unit: '',
enable_shelf_life: false,
shelf_life_days: null,
shelf_life_warning_days: 0,
enable_inventory_warning: false,
inventory_upper: null,
inventory_lower: null,
is_active: true,
remark: '',
},
showUnitPicker: false,
showCategoryPicker: false,
unitItems: [],
categoryItems: [],
}
},
methods: {
update() {
if (!this.dataForm.number) {
this.$refs.uToast.show({ message: '请填写编号' });
return
}
if (!this.dataForm.name) {
this.$refs.uToast.show({ message: '请填写名称' });
return
}
productUpdate(this.dataForm).then((data) => {
uni.showToast({ title: '保存成功', duration: 2000 });
uni.navigateBack();
this.eventChannel.emit('onUpdate', data);
});
},
changeUnit(item) {
this.showUnitPicker = false;
this.dataForm.unit = this.unitItems[item.indexs[0]].id;
this.dataForm.unit_name = this.unitItems[item.indexs[0]].name;
},
changeCategory(item) {
this.showCategoryPicker = false;
this.dataForm.category = this.categoryItems[item.indexs[0]].id;
this.dataForm.category_name = this.categoryItems[item.indexs[0]].name;
},
},
onShow() {
unitOption({ page: 1, page_size: 999999 }).then((data) => {
this.unitItems = data.results;
});
categoryOption({ page: 1, page_size: 999999 }).then((data) => {
this.categoryItems = data.results;
});
},
onLoad(option) {
this.eventChannel = this.getOpenerEventChannel();
this.dataForm = { ...JSON.parse(decodeURIComponent(option.item)) };
},
}
</script>
<style scoped>
</style>

View file

@ -1,344 +0,0 @@
<template>
<view>
<view style="padding: 0 24rpx; margin-bottom: 100rpx;">
<u--form labelWidth="75">
<u-form-item label="盘点单号:" borderBottom>
<u--input v-model="form.number" border="none"></u--input>
</u-form-item>
<u-form-item label="仓库:" borderBottom @click="showWarehousePicker = true">
<u--input :value="form.warehouse_name" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="经手人:" borderBottom @click="showUserPicker = true">
<u--input :value="form.handler_name" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="处理日期:" borderBottom @click="showDatePicker = true">
<u--input :value="form.handle_time" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="备注:" borderBottom>
<u--input v-model="form.remark" border="none"></u--input>
</u-form-item>
<u-form-item label="产品编号:" borderBottom>
<u--input border="none" :focus="inputFocus1" @focus="inputFocus1 = true" @blur="inputFocus1 = false"
suffixIcon="scan" selectionStart="0" selectionEnd="999" placeholder="请扫描产品条形码" @confirm="scanGoods">
</u--input>
</u-form-item>
</u--form>
<u-cell v-for="(item, index) in stockCheckGoodsItems">
<view class="cell-title" slot="title">{{ item.goods_number }}</view>
<view class="cell-label" slot="label">
<u-row class="cell-label-row">
<u-col span="6">产品名称: {{ item.goods_name }}</u-col>
<u-col span="6">产品条码: {{ item.goods_barcode }}</u-col>
</u-row>
<view v-if="item.enable_batch_control">
<view v-for="_item in item.stock_check_batch_items" style="border: 1px solid #999; margin-top: 8rpx;">
<u-row class="cell-label-row">
<u-col span="6">批次编号: {{ _item.batch_number }}</u-col>
</u-row>
<u-row class="cell-label-row">
<u-col span="6">账面数量: {{ _item.book_quantity }}</u-col>
<u-col span="6">实际数量: {{ _item.actual_quantity }}</u-col>
</u-row>
</view>
</view>
<view v-else>
<u-row class="cell-label-row">
<u-col span="6">账面数量: {{ item.book_quantity }}</u-col>
<u-col span="6">实际数量: {{ item.actual_quantity }}</u-col>
</u-row>
</view>
</view>
</u-cell>
</view>
<view style="position: fixed; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="确认盘点" :disabled="createLoading" :loading="createLoading"
@click="create">
</u-button>
</view>
<u-datetime-picker :show="showDatePicker" mode="date" :closeOnClickOverlay="true" @close="showDatePicker = false"
@cancel="showDatePicker = false" @confirm="selectDatePicker">
</u-datetime-picker>
<u-picker :show="showWarehousePicker" :columns="[warehouseItems]" keyName="name" :closeOnClickOverlay="true"
@close="showWarehousePicker = false" @cancel="showWarehousePicker = false" @confirm="selectWarehouse"></u-picker>
<u-picker :show="showUserPicker" :columns="[userItems]" keyName="name" :closeOnClickOverlay="true"
@close="showUserPicker = false" @cancel="showUserPicker = false" @confirm="selectUser"></u-picker>
<u-picker :show="showBatchPicker" :columns="[batchItems]" keyName="number" :closeOnClickOverlay="true"
@close="showBatchPicker = false" @cancel="showBatchPicker = false" @confirm="selectBatch"></u-picker>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import { userOption, warehouseOption, batchOption, inventoryOption } from '@/api/option.js';
import { stockCheckNumber, stockCheckCreate } from '@/api/stockCheck.js';
export default {
data() {
return {
eventChannel: null,
showWarehousePicker: false,
showUserPicker: false,
showDatePicker: false,
showBatchPicker: false,
inputFocus1: false,
warehouseItems: [],
userItems: [],
form: {},
stockCheckGoodsItems: [],
batchItems: [],
goodsItem: {},
createLoading: false,
}
},
methods: {
initData() {
warehouseOption({ page_size: 999999, is_active: true }).then(data => {
this.warehouseItems = data.results;
});
userOption({ page_size: 999999, is_active: true }).then(data => {
this.userItems = data.results;
});
stockCheckNumber().then(data => {
this.form.number = data.number;
});
},
validateGoods(value) {
if (this.stockOutGoodsItem.goods_number == value) {
this.stockOutGoodsItem.stockOutQuantity += 1;
this.$refs.uToast.show({ message: '扫码成功' });
} else {
this.$refs.uToast.show({ message: '产品错误' });
}
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
},
confirm() {
if (this.stockOutGoodsItem.enable_batch_control && !this.stockOutGoodsItem.batch) {
this.$refs.uToast.show({ message: '请选择批次' });
return;
}
this.stockOutGoodsItem.isConfirmed = true;
this.eventChannel.emit('confirm', this.stockOutGoodsItem);
uni.navigateBack();
},
selectWarehouse(item) {
let index = item.indexs[0];
let warehouseItem = this.warehouseItems[index];
this.form.warehouse = warehouseItem.id;
this.form.warehouse_name = warehouseItem.name;
this.showWarehousePicker = false;
},
selectUser(item) {
let index = item.indexs[0];
let userItem = this.userItems[index];
this.form.handler = userItem.id;
this.form.handler_name = userItem.name;
this.showUserPicker = false;
},
selectDatePicker(item) {
this.form.handle_time = this.formatDate(item.value);
this.showDatePicker = false;
},
scanGoods(value) {
if (!this.form.warehouse) {
this.$refs.uToast.show({ message: `请选择仓库` });
return;
}
for (let stockCheckGoodsItem of this.stockCheckGoodsItems) {
if (value == stockCheckGoodsItem.goods_number) {
if (stockCheckGoodsItem.enable_batch_control) {
this.batchItems = stockCheckGoodsItem.batchItems;
if (this.batchItems.length == 1) {
let stockCheckBatchItem = stockCheckGoodsItem.stock_check_batch_items[0];
stockCheckBatchItem.actual_quantity += 1;
stockCheckGoodsItem.actual_quantity += 1;
} else {
this.showBatchPicker = true;
return;
}
} else {
stockCheckGoodsItem.actual_quantity += 1;
}
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
return;
}
}
let searchForm = {
goods_number: value,
page: 1,
page_size: 6,
is_active: true,
warehouse: this.form.warehouse,
};
inventoryOption(searchForm).then((data) => {
if (data.results.length == 1) {
let goodsItem = data.results[0];
this.goodsItem = goodsItem;
if (goodsItem.enable_batch_control) {
this.getBatch(goodsItem);
return;
} else {
this.stockCheckGoodsItems.push({
goods: goodsItem.goods,
goods_number: goodsItem.goods_number,
goods_name: goodsItem.goods_name,
goods_barcode: goodsItem.goods_barcode,
enable_batch_control: goodsItem.enable_batch_control,
book_quantity: goodsItem.total_quantity,
actual_quantity: 1,
stock_check_batch_items: [],
});
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
}
} else {
this.$refs.uToast.show({ message: `产品不存在或库存为零` });
}
});
},
getBatch(goodsItem) {
let searchForm = {
page: 1,
page_size: 999999,
has_stock: true,
warehouse: this.form.warehouse,
goods: goodsItem.goods,
};
batchOption(searchForm).then((data) => {
this.batchItems = data.results;
if (this.batchItems.length == 0) {
this.$refs.uToast.show({ message: `产品不存在或库存为零` });
} else {
if (this.batchItems.length == 1) {
let batchItem = this.batchItems[0];
this.stockCheckGoodsItems.push({
goods: goodsItem.goods,
goods_number: goodsItem.goods_number,
goods_name: goodsItem.goods_name,
goods_barcode: goodsItem.goods_barcode,
enable_batch_control: goodsItem.enable_batch_control,
book_quantity: goodsItem.total_quantity,
actual_quantity: 1,
stock_check_batch_items: [{
id: batchItem.id,
batch_number: batchItem.number,
book_quantity: batchItem.remain_quantity,
actual_quantity: 1,
}],
batchItems: [...this.batchItems],
});
} else {
this.showBatchPicker = true;
}
}
});
},
selectBatch(item) {
this.showBatchPicker = false;
let batchItem = this.batchItems[item.indexs[0]];
let index = this.stockCheckGoodsItems.findIndex((item) => item.goods == batchItem.goods);
if (index == -1) {
this.stockCheckGoodsItems.push({
goods: this.goodsItem.goods,
goods_number: this.goodsItem.goods_number,
goods_name: this.goodsItem.goods_name,
goods_barcode: this.goodsItem.goods_barcode,
enable_batch_control: this.goodsItem.enable_batch_control,
book_quantity: this.goodsItem.total_quantity,
actual_quantity: 1,
stock_check_batch_items: [{
id: batchItem.id,
batch_number: batchItem.number,
book_quantity: batchItem.remain_quantity,
actual_quantity: 1,
}],
batchItems: [...this.batchItems],
});
} else {
let goodsItem = this.stockCheckGoodsItems[index];
goodsItem.actual_quantity += 1;
let batchIndex = goodsItem.stock_check_batch_items.findIndex((item) => item.id == batchItem.id);
if (batchIndex == -1) {
goodsItem.stock_check_batch_items.push({
id: batchItem.id,
batch_number: batchItem.number,
book_quantity: batchItem.remain_quantity,
actual_quantity: 1,
});
} else {
let stockCheckBatchItem = goodsItem.stock_check_batch_items[batchIndex];
}
}
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
},
formatDate(datetime) {
let date = new Date(datetime);
let year = date.getFullYear(),
month = ("0" + (date.getMonth() + 1)).slice(-2),
sdate = ("0" + date.getDate()).slice(-2);
return year + "-" + month + "-" + sdate;
},
create() {
if (!this.form.handler) {
this.$refs.uToast.show({ message: '请选择经手人' });
return
}
if (!this.form.handle_time) {
this.$refs.uToast.show({ message: '请选择处理日期' });
return
}
if (this.stockCheckGoodsItems.length == 0) {
this.$refs.uToast.show({ message: '请盘点物料' });
return
}
let createForm = {
...this.form,
stock_check_goods_Items: this.stockCheckGoodsItems,
};
console.log('===', createForm)
uni.showLoading({ title: '创建中' });
this.createLoading = true;
stockCheckCreate(createForm).then(() => {
uni.redirectTo({ url: '../stockCheckForm/index' });
}).finally(() => {
this.createLoading = false;
uni.hideLoading();
});
},
},
mounted() {
this.form = {};
this.initData();
}
}
</script>
<style scoped>
</style>

View file

@ -1,108 +0,0 @@
<template>
<view>
<view style="padding: 0 24rpx;">
<u--form labelWidth="75">
<u-form-item label="产品编号:" borderBottom>
<u--input border="none" :focus="inputFocus1" @focus="inputFocus1 = true" @blur="inputFocus1 = false"
suffixIcon="scan" selectionStart="0" selectionEnd="999" placeholder="请扫描产品条形码" @confirm="validateGoods">
</u--input>
</u-form-item>
<u-form-item label="产品名称:" borderBottom>
<u--input :value="stockInGoodsItem.goods_name" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item v-if="stockInGoodsItem.enable_batch_control" label="批次编号:" borderBottom
@click="showPicker = true;">
<u--input :value="stockInGoodsItem.batch_number" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="剩余数量:" borderBottom>
<u--input :value="stockInGoodsItem.remain_quantity" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="出库数量:" borderBottom>
<u--input v-model="stockInGoodsItem.stockInQuantity" border="none"></u--input>
</u-form-item>
<u-form-item v-if="stockInGoodsItem.enable_batch_control" label="批次编号:" borderBottom>
<u--input :value="stockInGoodsItem.batch_number" border="none"></u--input>
</u-form-item>
<u-form-item v-if="stockInGoodsItem.enable_batch_control" label="生产日期:" borderBottom
@click="showDatePicker = true">
<u--input :value="stockInGoodsItem.production_date" border="none" :disabled="true"></u--input>
</u-form-item>
</u--form>
</view>
<view style="position: absolute; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="确定" @click="confirm"></u-button>
</view>
<u-datetime-picker :show="showDatePicker" mode="date" :closeOnClickOverlay="true" @close="showDatePicker = false"
@cancel="showDatePicker = false" @confirm="selectDatePicker">
</u-datetime-picker>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import { batchOption } from '@/api/option.js';
export default {
data() {
return {
eventChannel: null,
stockInGoodsItem: {},
showDatePicker: false,
showPicker: false,
inputFocus1: true,
batchItems: []
}
},
methods: {
validateGoods(value) {
if (this.stockInGoodsItem.goods_number == value) {
this.stockInGoodsItem.stockInQuantity += 1;
this.$refs.uToast.show({ message: '扫码成功' });
} else {
this.$refs.uToast.show({ message: '产品错误' });
}
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
},
confirm() {
if (this.stockInGoodsItem.enable_batch_control && !this.stockInGoodsItem.batch_number &&
this.stockInGoodsItem.batch_number.length == 0) {
this.$refs.uToast.show({ message: '请输入批次' });
return;
}
this.stockInGoodsItem.isConfirmed = true;
this.eventChannel.emit('confirm', this.stockInGoodsItem);
uni.navigateBack();
},
selectDatePicker(item) {
this.stockInGoodsItem.production_date = this.formatDate(item.value);
this.showDatePicker = false;
},
formatDate(datetime) {
let date = new Date(datetime);
let year = date.getFullYear(),
month = ("0" + (date.getMonth() + 1)).slice(-2),
sdate = ("0" + date.getDate()).slice(-2);
return year + "-" + month + "-" + sdate;
},
},
onLoad(option) {
this.eventChannel = this.getOpenerEventChannel();
this.stockInGoodsItem = JSON.parse(decodeURIComponent(option.item));
if (this.stockInGoodsItem.enable_batch_control) {
this.getBatch();
}
},
}
</script>
<style scoped>
</style>

View file

@ -1,241 +0,0 @@
<template>
<view>
<view style="padding: 0 24rpx;">
<u--form labelWidth="110">
<u-form-item label="入库单号:" borderBottom>
<u--input border="none" :focus="inputFocus1" @focus="inputFocus1 = true" @blur="inputFocus1 = false"
suffixIcon="scan" selectionStart="0" selectionEnd="999" placeholder="请扫描入库单条形码" @confirm="getStockInOrder">
</u--input>
</u-form-item>
<u-form-item label="应入数量:" borderBottom>
<u--input :value="totalQuantity" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="已入数量:" borderBottom>
<u--input :value="inBoundQuantity" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="产品编号:" borderBottom>
<u--input border="none" :focus="inputFocus2" @focus="inputFocus2 = true" @blur="inputFocus2 = false"
suffixIcon="scan" selectionStart="0" selectionEnd="999" placeholder="请扫描产品条形码" @confirm="scanGoods">
</u--input>
</u-form-item>
</u--form>
<u-cell v-for="(item, index) in stockInGoodsItems" isLink @click="goGoodsForm(item)">
<view class="cell-title" slot="title">{{ item.goods_number }}</view>
<view class="cell-label" slot="label">
<u-row class="cell-label-row">
<u-col span="6">产品名称: {{ item.goods_name }}</u-col>
<u-col span="6">产品条码: {{ item.goods_barcode }}</u-col>
</u-row>
<u-row class="cell-label-row">
<u-col span="6">应收数量: {{ item.remain_quantity }}</u-col>
<u-col span="6">已收数量: {{ item.stockInQuantity }}</u-col>
</u-row>
</view>
</u-cell>
</view>
<view style="position: absolute; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="确认入库" :disabled="createLoading" :loading="createLoading"
@click="create">
</u-button>
</view>
<u-toast ref="uToast"></u-toast>
<u-picker :show="showPicker" :columns="[batchItems]" keyName="batch_number" :closeOnClickOverlay="true"
@close="showPicker = false" @cancel="showPicker = false" @confirm="selectBatch"></u-picker>
</view>
</template>
<script>
import { stockInTaskList, stockInRecordCreate } from '@/api/stockIn.js';
import { mapState, mapMutations } from 'vuex';
export default {
data() {
return {
stockInOrderItem: {},
stockInGoodsItems: [],
createLoading: false,
batchItems: [],
showPicker: false,
createLoading: false,
inputFocus1: true,
inputFocus2: false,
}
},
computed: {
...mapState({
userItem: (state) => state.system.userItem,
warehouseItems: (state) => state.system.warehouseItems,
currentWarehouse: (state) => state.system.currentWarehouse,
}),
totalQuantity() {
let totalQuantity = 0;
for (let item of this.stockInGoodsItems) {
totalQuantity += Number(item.remain_quantity);
}
return totalQuantity
},
inBoundQuantity() {
let inBoundQuantity = 0;
for (let item of this.stockInGoodsItems) {
inBoundQuantity += Number(item.stockInQuantity);
}
return inBoundQuantity
},
},
methods: {
getStockInOrder(value) {
let searchForm = {
search: value,
page: 1,
page_size: 6,
is_completed: false,
is_void: false
};
uni.showLoading({ title: '查询中' });
stockInTaskList(searchForm).then((data) => {
if (data.count == 1) {
this.stockInOrderItem = data.results[0];
let stockInGoodsItems = [];
for (let item of this.stockInOrderItem.stock_in_goods_items) {
item.stockInQuantity = 0;
item.isConfirmed = false;
item.warehouse = this.stockInOrderItem.warehouse;
stockInGoodsItems.push(item);
}
this.stockInGoodsItems = stockInGoodsItems;
this.inputFocus2 = true;
} else {
this.$refs.uToast.show({ message: `入库通知单号[${value}]不存在` });
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
}
}).finally(() => {
uni.hideLoading();
});
},
goGoodsForm(stockInGoodsItem) {
const updateReceivedGoods = (item) => {
let index = this.stockInGoodsItems.findIndex(_item => _item.id == item.id);
if (index == -1) {
this.stockInGoodsItems.splice(0, 0, item);
} else {
this.stockInGoodsItems.splice(index, 1, item);
}
}
uni.navigateTo({
url: '../stockInForm/index?item=' + encodeURIComponent(JSON.stringify(stockInGoodsItem)),
events: {
confirm(item) {
updateReceivedGoods(item);
},
},
});
},
scanGoods(value) {
let batchItems = [];
for (let item of this.stockInGoodsItems) {
if (item.goods_number == value) {
batchItems.push(item);
}
}
if (batchItems.length == 0) {
this.$refs.uToast.show({ message: `物料[${value}]不存在` });
setTimeout(() => {
this.inputFocus2 = true;
}, 500);
} else if (batchItems.length == 1) {
let batchItem = batchItems[0];
this.goGoodsForm(batchItem);
} else {
this.batchItems = batchItems;
this.showPicker = true;
}
},
selectBatch(item) {
this.showPicker = false;
let batchItem = this.batchItems[item.indexs[0]];
this.goGoodsForm(batchItem);
},
getTime() {
let date = new Date(),
year = date.getFullYear(),
month = date.getMonth() + 1,
day = date.getDate(),
hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(),
minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(),
second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
month >= 1 && month <= 9 ? (month = "0" + month) : "";
day >= 0 && day <= 9 ? (day = "0" + day) : "";
let timer = year + '-' + month + '-' + day;
return timer;
},
create() {
let createForm = {
stock_in_order: this.stockInOrderItem.id,
handler: this.userItem.id,
handle_time: this.getTime(),
stock_in_record_goods_items: [],
};
for (let item of this.stockInGoodsItems) {
if (item.isConfirmed && item.stockInQuantity > 0) {
createForm.stock_in_record_goods_items.push({
stock_in_goods: item.id,
batch_number: item.batch_number,
stock_in_quantity: item.stockInQuantity,
production_date: item.production_date,
});
}
}
if (createForm.stock_in_record_goods_items.length == 0) {
this.$refs.uToast.show({ message: '请添加入库产品' });
return
}
uni.showLoading({ title: '创建中' });
this.createLoading = true;
stockInRecordCreate(createForm).then(() => {
uni.redirectTo({ url: '../stockInTask/index' });
}).finally(() => {
this.createLoading = false;
uni.hideLoading();
});
},
},
onShow() {
if (this.stockInGoodsItems.length > 0) {
setTimeout(() => {
this.inputFocus2 = true;
}, 500);
}
},
}
</script>
<style>
.cell-title {
font-size: 32rpx;
font-weight: 500;
margin-bottom: 12rpx;
}
.cell-label {
font-size: 24rpx;
color: #0005;
}
.cell-label-row {
margin-bottom: 8rpx;
}
</style>

View file

@ -1,109 +0,0 @@
<template>
<view>
<view style="padding: 0 24rpx;">
<u--form labelWidth="75">
<u-form-item label="产品编号:" borderBottom>
<u--input border="none" :focus="inputFocus1" @focus="inputFocus1 = true" @blur="inputFocus1 = false"
suffixIcon="scan" selectionStart="0" selectionEnd="999" placeholder="请扫描产品条形码" @confirm="validateGoods">
</u--input>
</u-form-item>
<u-form-item label="产品名称:" borderBottom>
<u--input :value="stockOutGoodsItem.goods_name" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item v-if="stockOutGoodsItem.enable_batch_control" label="批次编号:" borderBottom
@click="showPicker = true;">
<u--input :value="stockOutGoodsItem.batch_number" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="剩余数量:" borderBottom>
<u--input :value="stockOutGoodsItem.remain_quantity" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="出库数量:" borderBottom>
<u--input v-model="stockOutGoodsItem.stockOutQuantity" border="none"></u--input>
</u-form-item>
</u--form>
</view>
<view style="position: absolute; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="确定" @click="confirm"></u-button>
</view>
<u-picker :show="showPicker" :columns="[batchItems]" keyName="number" :closeOnClickOverlay="true"
@close="showPicker = false" @cancel="showPicker = false" @confirm="selectPicker"></u-picker>
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
import { batchOption } from '@/api/option.js';
export default {
data() {
return {
eventChannel: null,
stockOutGoodsItem: {},
showDatePicker: false,
showPicker: false,
inputFocus1: true,
batchItems: []
}
},
methods: {
validateGoods(value) {
if (this.stockOutGoodsItem.goods_number == value) {
this.stockOutGoodsItem.stockOutQuantity += 1;
this.$refs.uToast.show({ message: '扫码成功' });
} else {
this.$refs.uToast.show({ message: '产品错误' });
}
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
},
confirm() {
if (this.stockOutGoodsItem.enable_batch_control && !this.stockOutGoodsItem.batch) {
this.$refs.uToast.show({ message: '请选择批次' });
return;
}
this.stockOutGoodsItem.isConfirmed = true;
this.eventChannel.emit('confirm', this.stockOutGoodsItem);
uni.navigateBack();
},
selectPicker(item) {
let index = item.indexs[0];
let batchItem = this.batchItems[index];
this.stockOutGoodsItem.batch = batchItem.id;
this.stockOutGoodsItem.batch_number = `${batchItem.number} | 库存: ${batchItem.remain_quantity}`;
this.showPicker = false;
},
getBatch() {
let searchForm = {
page: 1,
page_size: 999999,
has_stock: true,
warehouse: this.stockOutGoodsItem.warehouse,
goods: this.stockOutGoodsItem.goods,
};
batchOption(searchForm).then((data) => {
this.batchItems = data.results;
console.log('---------', this.batchItems)
});
},
},
onLoad(option) {
this.eventChannel = this.getOpenerEventChannel();
this.stockOutGoodsItem = JSON.parse(decodeURIComponent(option.item));
if (this.stockOutGoodsItem.enable_batch_control) {
this.getBatch();
}
},
}
</script>
<style scoped>
</style>

View file

@ -1,240 +0,0 @@
<template>
<view>
<view style="padding: 0 24rpx;">
<u--form labelWidth="110">
<u-form-item label="出库单号:" borderBottom>
<u--input border="none" :focus="inputFocus1" @focus="inputFocus1 = true" @blur="inputFocus1 = false"
suffixIcon="scan" selectionStart="0" selectionEnd="999" placeholder="请扫描出库单条形码" @confirm="getStockOutOrder">
</u--input>
</u-form-item>
<u-form-item label="应出数量:" borderBottom>
<u--input :value="totalQuantity" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="已出数量:" borderBottom>
<u--input :value="outBoundQuantity" border="none" :disabled="true"></u--input>
</u-form-item>
<u-form-item label="产品编号:" borderBottom>
<u--input border="none" :focus="inputFocus2" @focus="inputFocus2 = true" @blur="inputFocus2 = false"
suffixIcon="scan" selectionStart="0" selectionEnd="999" placeholder="请扫描产品条形码" @confirm="scanGoods">
</u--input>
</u-form-item>
</u--form>
<u-cell v-for="(item, index) in stockOutGoodsItems" isLink @click="goGoodsForm(item)">
<view class="cell-title" slot="title">{{ item.goods_number }}</view>
<view class="cell-label" slot="label">
<u-row class="cell-label-row">
<u-col span="6">产品名称: {{ item.goods_name }}</u-col>
<u-col span="6">产品条码: {{ item.goods_barcode }}</u-col>
</u-row>
<u-row class="cell-label-row">
<u-col span="6">应收数量: {{ item.remain_quantity }}</u-col>
<u-col span="6">已收数量: {{ item.stockOutQuantity }}</u-col>
</u-row>
</view>
</u-cell>
</view>
<view style="position: absolute; bottom: 0; width: 100%;">
<u-button type="primary" size="large" text="确认出库" :disabled="createLoading" :loading="createLoading"
@click="create">
</u-button>
</view>
<u-toast ref="uToast"></u-toast>
<u-picker :show="showPicker" :columns="[batchItems]" keyName="batch_number" :closeOnClickOverlay="true"
@close="showPicker = false" @cancel="showPicker = false" @confirm="selectBatch"></u-picker>
</view>
</template>
<script>
import { stockOutTaskList, stockOutRecordCreate } from '@/api/stockOut.js';
import { mapState, mapMutations } from 'vuex';
export default {
data() {
return {
stockOutOrderItem: {},
stockOutGoodsItems: [],
createLoading: false,
batchItems: [],
showPicker: false,
createLoading: false,
inputFocus1: true,
inputFocus2: false,
}
},
computed: {
...mapState({
userItem: (state) => state.system.userItem,
warehouseItems: (state) => state.system.warehouseItems,
currentWarehouse: (state) => state.system.currentWarehouse,
}),
totalQuantity() {
let totalQuantity = 0;
for (let item of this.stockOutGoodsItems) {
totalQuantity += Number(item.remain_quantity);
}
return totalQuantity
},
outBoundQuantity() {
let outBoundQuantity = 0;
for (let item of this.stockOutGoodsItems) {
outBoundQuantity += Number(item.stockOutQuantity);
}
return outBoundQuantity
},
},
methods: {
getStockOutOrder(value) {
let searchForm = {
search: value,
page: 1,
page_size: 6,
is_completed: false,
is_void: false
};
uni.showLoading({ title: '查询中' });
stockOutTaskList(searchForm).then((data) => {
if (data.count == 1) {
this.stockOutOrderItem = data.results[0];
let stockOutGoodsItems = [];
for (let item of this.stockOutOrderItem.stock_out_goods_items) {
item.stockOutQuantity = 0;
item.isConfirmed = false;
item.warehouse = this.stockOutOrderItem.warehouse;
stockOutGoodsItems.push(item);
}
this.stockOutGoodsItems = stockOutGoodsItems;
this.inputFocus2 = true;
} else {
this.$refs.uToast.show({ message: `出库通知单号[${value}]不存在` });
setTimeout(() => {
this.inputFocus1 = true;
}, 500);
}
}).finally(() => {
uni.hideLoading();
});
},
goGoodsForm(stockOutGoodsItem) {
const updateReceivedGoods = (item) => {
let index = this.stockOutGoodsItems.findIndex(_item => _item.id == item.id);
if (index == -1) {
this.stockOutGoodsItems.splice(0, 0, item);
} else {
this.stockOutGoodsItems.splice(index, 1, item);
}
}
uni.navigateTo({
url: '../stockOutForm/index?item=' + encodeURIComponent(JSON.stringify(stockOutGoodsItem)),
events: {
confirm(item) {
updateReceivedGoods(item);
},
},
});
},
scanGoods(value) {
let batchItems = [];
for (let item of this.stockOutGoodsItems) {
if (item.goods_number == value) {
batchItems.push(item);
}
}
if (batchItems.length == 0) {
this.$refs.uToast.show({ message: `物料[${value}]不存在` });
setTimeout(() => {
this.inputFocus2 = true;
}, 500);
} else if (batchItems.length == 1) {
let batchItem = batchItems[0];
this.goGoodsForm(batchItem);
} else {
this.batchItems = batchItems;
this.showPicker = true;
}
},
selectBatch(item) {
this.showPicker = false;
let batchItem = this.batchItems[item.indexs[0]];
this.goGoodsForm(batchItem);
},
getTime() {
let date = new Date(),
year = date.getFullYear(),
month = date.getMonth() + 1,
day = date.getDate(),
hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(),
minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(),
second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
month >= 1 && month <= 9 ? (month = "0" + month) : "";
day >= 0 && day <= 9 ? (day = "0" + day) : "";
let timer = year + '-' + month + '-' + day;
return timer;
},
create() {
let createForm = {
stock_out_order: this.stockOutOrderItem.id,
handler: this.userItem.id,
handle_time: this.getTime(),
stock_out_record_goods_items: [],
};
for (let item of this.stockOutGoodsItems) {
if (item.isConfirmed && item.stockOutQuantity > 0) {
createForm.stock_out_record_goods_items.push({
stock_out_goods: item.id,
batch: item.batch,
stock_out_quantity: item.stockOutQuantity,
});
}
}
if (createForm.stock_out_record_goods_items.length == 0) {
this.$refs.uToast.show({ message: '请添加收货物料' });
return
}
uni.showLoading({ title: '创建中' });
this.createLoading = true;
stockOutRecordCreate(createForm).then(() => {
uni.redirectTo({ url: '../stockOutTask/index' });
}).finally(() => {
this.createLoading = false;
uni.hideLoading();
});
},
},
onShow() {
if (this.stockOutGoodsItems.length > 0) {
setTimeout(() => {
this.inputFocus2 = true;
}, 500);
}
},
}
</script>
<style>
.cell-title {
font-size: 32rpx;
font-weight: 500;
margin-bottom: 12rpx;
}
.cell-label {
font-size: 24rpx;
color: #0005;
}
.cell-label-row {
margin-bottom: 8rpx;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -1,11 +0,0 @@
import system from './modules/system';
import Vuex from 'vuex';
import Vue from 'vue';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
system,
},
})

View file

@ -1,18 +0,0 @@
export default {
state: () => ({
userItem: {},
warehouseItems: [],
currentWarehouse: undefined,
}),
mutations: {
setUserItem(state, data) {
state.userItem = data;
},
setWarehouseItems(state, data) {
state.warehouseItems = data;
},
setCurrentWarehouse(state, data) {
state.currentWarehouse = data;
},
}
}

View file

@ -1,77 +0,0 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
@import '@/uni_modules/uview-ui/theme.scss';
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 www.uviewui.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,104 +0,0 @@
<p align="center">
<img alt="logo" src="https://uviewui.com/common/logo.png" width="120" height="120" style="margin-bottom: 10px;">
</p>
<h3 align="center" style="margin: 30px 0 30px;font-weight: bold;font-size:40px;">uView</h3>
<h3 align="center">多平台快速开发的UI框架</h3>
## 说明
uView UI是[uni-app](https://uniapp.dcloud.io/)生态优秀的UI框架全面的组件和便捷的工具会让您信手拈来如鱼得水
## 特性
- 兼容安卓iOS微信小程序H5QQ小程序百度小程序支付宝小程序头条小程序
- 60+精选组件,功能丰富,多端兼容,让您快速集成,开箱即用
- 众多贴心的JS利器让您飞镖在手召之即来百步穿杨
- 众多的常用页面和布局,让您专注逻辑,事半功倍
- 详尽的文档支持,现代化的演示效果
- 按需引入,精简打包体积
## 安装
```bash
# npm方式安装插件市场导入无需执行此命令
npm i uview-ui
```
## 快速上手
1. `main.js`引入uView库
```js
// main.js
import uView from 'uview-ui';
Vue.use(uView);
```
2. `App.vue`引入基础样式(注意style标签需声明scss属性支持)
```css
/* App.vue */
<style lang="scss">
@import "uview-ui/index.scss";
</style>
```
3. `uni.scss`引入全局scss变量文件
```css
/* uni.scss */
@import "uview-ui/theme.scss";
```
4. `pages.json`配置easycom规则(按需引入)
```js
// pages.json
{
"easycom": {
// npm安装的方式不需要前面的"@/",下载安装的方式需要"@/"
// npm安装方式
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
// 下载安装方式
// "^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
},
// 此为本身已有的内容
"pages": [
// ......
]
}
```
请通过[快速上手](https://www.uviewui.com/components/quickstart.html)了解更详细的内容
## 使用方法
配置easycom规则后自动按需引入无需`import`组件,直接引用即可。
```html
<template>
<u-button text="按钮"></u-button>
</template>
```
请通过[快速上手](https://www.uviewui.com/components/quickstart.html)了解更详细的内容
## 链接
- [官方文档](https://www.uviewui.com/)
- [更新日志](https://www.www.uviewui.com/components/changelog.html)
- [升级指南](https://www.uviewui.com/components/changelog.html)
- [关于我们](https://www.uviewui.com/cooperation/about.html)
## 预览
您可以通过**微信**扫码,查看最佳的演示效果。
<br>
<br>
<img src="https://uviewui.com/common/weixin_mini_qrcode.png" width="220" height="220" >
## 捐赠uView的研发
uView文档和源码全部开源免费如果您认为uView帮到了您的开发工作您可以捐赠uView的研发工作捐赠无门槛哪怕是一杯可乐也好(相信这比打赏主播更有意义)。
<img src="https://uviewui.com/common/alipay.png" width="220" ><img style="margin-left: 100px;" src="https://uviewui.com/common/wechat.png" width="220" >
## 版权信息
uView遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议意味着您无需支付任何费用也无需授权即可将uView应用到您的产品中。

View file

@ -1,293 +0,0 @@
## 2.0.292022-03-13
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复`u--text`组件设置`decoration`属性未生效的问题
2. 修复`u-datetime-picker`使用`formatter`后返回值不正确
3. 修复`u-datetime-picker` `intercept` 可能为undefined
4. 修复已设置单位 uni..config.unit = 'rpx'时,线型指示器 `transform` 的位置翻倍,导致指示器超出宽度
5. 修复mixin中bem方法生成的类名在支付宝和字节小程序中失效
6. 修复默认值传值为空的时候,打开`u-datetime-picker`报错不能选中第一列时间的bug
7. 修复`u-datetime-picker`使用`formatter`后返回值不正确
8. 修复`u-image`组件`loading`无效果的问题
9. 修复`config.unit`属性设为`rpx`时,导航栏占用高度不足导致塌陷的问题
10. 修复`u-datetime-picker`组件`itemHeight`无效问题
11. 其他修复
## 2.0.282022-02-22
# uView2.0重磅发布,利剑出鞘,一统江湖
1. search组件新增searchIconSize属性
2. 兼容Safari/Webkit中传入时间格式如2022-02-17 12:00:56
3. 修复text value.js 判断日期出format错误问题
4. priceFormat格式化金额出现精度错误
5. priceFormat在部分情况下出现精度损失问题
6. 优化表单rules提示
7. 修复avatar组件src为空时展示状态不对
8. 其他修复
## 2.0.272022-01-28
# uView2.0重磅发布,利剑出鞘,一统江湖
1.样式修复
## 2.0.262022-01-28
# uView2.0重磅发布,利剑出鞘,一统江湖
1.样式修复
## 2.0.252022-01-27
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复text组件mode=price时可能会导致精度错误的问题
2. 添加$u.setConfig()方法可设置uView内置的config, props, zIndex, color属性详见[修改uView内置配置方案](https://uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
3. 优化form组件在errorType=toast时如果输入错误页面会有抖动的问题
4. 修复$u.addUnit()对配置默认单位可能无效的问题
## 2.0.242022-01-25
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复swiper在current指定非0时缩放有误
2. 修复u-icon添加stop属性的时候报错
3. 优化遗留的通过正则判断rpx单位的问题
4. 优化Layout布局 vue使用gutter时会超出固定区域
5. 优化search组件高度单位问题rpx -> px
6. 修复u-image slot 加载和错误的图片失去了高度
7. 修复u-index-list中footer插槽与header插槽存在性判断错误
8. 修复部分机型下u-popup关闭时会闪烁
9. 修复u-image在nvue-app下失去宽高
10. 修复u-popup运行报错
11. 修复u-tooltip报错
12. 修复box-sizing在app下的警告
13. 修复u-navbar在小程序中报运行时错误
14. 其他修复
## 2.0.232022-01-24
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复image组件在hx3.3.9的nvue下可能会显示异常的问题
2. 修复col组件gutter参数带rpx单位处理不正确的问题
3. 修复text组件单行时无法显示省略号的问题
4. navbar添加titleStyle参数
5. 升级到hx3.3.9可消除nvue下控制台样式警告的问题
## 2.0.222022-01-19
# uView2.0重磅发布,利剑出鞘,一统江湖
1. $u.page()方法优化,避免在特殊场景可能报错的问题
2. picker组件添加immediateChange参数
3. 新增$u.pages()方法
## 2.0.212022-01-19
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 优化form组件在用户设置rules的时候提示用户model必传
2. 优化遗留的通过正则判断rpx单位的问题
3. 修复微信小程序环境中tabbar组件开启safeAreaInsetBottom属性后placeholder高度填充不正确
4. 修复swiper在current指定非0时缩放有误
5. 修复u-icon添加stop属性的时候报错
6. 修复upload组件在accept=all的时候没有作用
7. 修复在text组件mode为phone时call属性无效的问题
8. 处理u-form clearValidate方法
9. 其他修复
## 2.0.202022-01-14
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复calendar默认会选择一个日期如果直接点确定的话无法取到值的问题
2. 修复Slider缺少disabled props 还有注释
3. 修复u-notice-bar点击事件无法拿到index索引值的问题
4. 修复u-collapse-item在vue文件下app端自定义插槽不生效的问题
5. 优化头像为空时显示默认头像
6. 修复图片地址赋值后判断加载状态为完成问题
7. 修复日历滚动到默认日期月份区域
8. search组件暴露点击左边icon事件
9. 修复u-form clearValidate方法不生效
10. upload h5端增加返回文件参数文件的name参数
11. 处理upload选择文件后url为blob类型无法预览的问题
12. u-code-input 修复输入框没有往左移出一半屏幕
13. 修复Upload上传 disabled为true时控制台报hoverClass类型错误
14. 临时处理ios app下grid点击坍塌问题
15. 其他修复
## 2.0.192021-12-29
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 优化微信小程序包体积可在微信中预览请升级HbuilderX3.3.4,同时在“运行->运行到小程序模拟器”中勾选“运行时是否压缩代码”
2. 优化微信小程序setData性能处理某些方法如$u.route()无法在模板中使用的问题
3. navbar添加autoBack参数
4. 允许avatar组件的事件冒泡
5. 修复cell组件报错问题
6. 其他修复
## 2.0.182021-12-28
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复app端编译报错问题
2. 重新处理微信小程序端setData过大的性能问题
3. 修复边框问题
4. 修复最大最小月份不大于0则没有数据出现的问题
5. 修复SwipeAction微信小程序端无法上下滑动问题
6. 修复input的placeholder在小程序端默认显示为true问题
7. 修复divider组件click事件无效问题
8. 修复u-code-input maxlength 属性值为 String 类型时显示异常
9. 修复当 grid只有 1到2时 在小程序端algin设置无效的问题
10. 处理form-item的label为top时取消错误提示的左边距
11. 其他修复
## 2.0.172021-12-26
## uView正在参与开源中国的“年度最佳项目”评选之前投过票的现在也可以投票恳请同学们投一票[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 解决HBuilderX3.3.3.20211225版本导致的样式问题
2. calendar日历添加monthNum参数
3. navbar添加center slot
## 2.0.162021-12-25
## uView正在参与开源中国的“年度最佳项目”评选之前投过票的现在也可以投票恳请同学们投一票[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 解决微信小程序setData性能问题
2. 修复count-down组件change事件不触发问题
## 2.0.152021-12-21
## uView正在参与开源中国的“年度最佳项目”评选之前投过票的现在也可以投票恳请同学们投一票[点此帮助uView](https://www.oschina.net/project/top_cn_2021/?id=583)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复Cell单元格titleWidth无效
2. 修复cheakbox组件ischecked不更新
3. 修复keyboard是否显示"."按键默认值问题
4. 修复number-keyboard是否显示键盘的"."符号问题
5. 修复Input输入框 readonly无效
6. 修复u-avatar 导致打包app、H5时候报错问题
7. 修复Upload上传deletable无效
8. 修复upload当设置maxSize时无效的问题
9. 修复tabs lineWidth传入带单位的字符串的时候偏移量计算错误问题
10. 修复rate组件在有padding的view内显示的星星位置和可触摸区域不匹配无法正常选中星星
## 2.0.132021-12-14
## [点击加群交流反馈364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复配置默认单位为rpx可能会导致自定义导航栏高度异常的问题
## 2.0.122021-12-14
## [点击加群交流反馈364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复tabs组件在vue环境下划线消失的问题
2. 修复upload组件在安卓小程序无法选择视频的问题
3. 添加uni.$u.config.unit配置用于配置参数默认单位详见[默认单位配置](https://www.uviewui.com/components/setting.html#%E9%BB%98%E8%AE%A4%E5%8D%95%E4%BD%8D%E9%85%8D%E7%BD%AE)
4. 修复textarea组件在没绑定v-model时字符统计不生效问题
5. 修复nvue下控制是否出现滚动条失效问题
## 2.0.112021-12-13
## [点击加群交流反馈364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. text组件align参数无效的问题
2. subsection组件添加keyName参数
3. upload组件无法判断[Object file]类型的问题
4. 处理notify层级过低问题
5. codeInput组件添加disabledDot参数
6. 处理actionSheet组件round参数无效的问题
7. calendar组件添加round参数用于控制圆角值
8. 处理swipeAction组件在vue环境下默认被打开的问题
9. button组件的throttleTime节流参数无效的问题
10. 解决u-notify手动关闭方法close()无效的问题
11. input组件readonly不生效问题
12. tag组件type参数为info不生效问题
## 2.0.102021-12-08
## [点击加群交流反馈364463526](https://jq.qq.com/?_chanwv=1027&k=mCxS3TGY)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复button sendMessagePath属性不生效
2. 修复DatetimePicker选择器title无效
3. 修复u-toast设置loading=true不生效
4. 修复u-text金额模式传0报错
5. 修复u-toast组件的icon属性配置不生效
6. button的icon在特殊场景下的颜色优化
7. IndexList优化增加#
## 2.0.92021-12-01
## [点击加群交流反馈232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 优化swiper的height支持100%值(仅vue有效)修复嵌入视频时click事件无法触发的问题
2. 优化tabs组件对list值为空的判断或者动态变化list时重新计算相关尺寸的问题
3. 优化datetime-picker组件逻辑让其后续打开的默认值为上一次的选中值需要通过v-model绑定值才有效
4. 修复upload内嵌在其他组件中选择图片可能不会换行的问题
## 2.0.82021-12-01
## [点击加群交流反馈232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复toast的position参数无效问题
2. 处理input在ios nvue上无法获得焦点的问题
3. avatar-group组件添加extraValue参数让剩余展示数量可手动控制
4. tabs组件添加keyName参数用于配置从对象中读取的键名
5. 处理text组件名字脱敏默认配置无效的问题
6. 处理picker组件item文本太长换行问题
## 2.0.72021-11-30
## [点击加群交流反馈232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 修复radio和checkbox动态改变v-model无效的问题。
2. 优化form规则validator在微信小程序用法
3. 修复backtop组件mode参数在微信小程序无效的问题
4. 处理Album的previewFullImage属性无效的问题
5. 处理u-datetime-picker组件mode='time'在选择改变时间时,控制台报错的问题
## 2.0.62021-11-27
## [点击加群交流反馈232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. 处理tag组件在vue下边框无效的问题。
2. 处理popup组件圆角参数可能无效的问题。
3. 处理tabs组件lineColor参数可能无效的问题。
4. propgress组件在值很小时显示异常的问题。
## 2.0.52021-11-25
## [点击加群交流反馈232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. calendar在vue下显示异常问题。
2. form组件labelPosition和errorType参数无效的问题
3. input组件inputAlign无效的问题
4. 其他一些修复
## 2.0.42021-11-23
## [点击加群交流反馈232041042](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
0. input组件缺失@confirm事件以及subfix和prefix无效问题
1. component.scss文件样式在vue下干扰全局布局问题
2. 修复subsection在vue环境下表现异常的问题
3. tag组件的bgColor等参数无效的问题
4. upload组件不换行的问题
5. 其他的一些修复处理
## 2.0.32021-11-16
## [点击加群交流反馈1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. uView2.0已实现全面兼容nvue
2. uView2.0对1.x进行了架构重构细节和性能都有极大提升
3. 目前uView2.0为公测阶段,相关细节可能会有变动
4. 我们写了一份与1.x的对比指南详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
5. 处理modal的confirm回调事件拼写错误问题
6. 处理input组件@input事件参数错误问题
7. 其他一些修复
## 2.0.22021-11-16
## [点击加群交流反馈1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. uView2.0已实现全面兼容nvue
2. uView2.0对1.x进行了架构重构细节和性能都有极大提升
3. 目前uView2.0为公测阶段,相关细节可能会有变动
4. 我们写了一份与1.x的对比指南详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
5. 修复input组件formatter参数缺失问题
6. 优化loading-icon组件的scss写法问题防止不兼容新版本scss
## 2.0.0(2020-11-15)
## [点击加群交流反馈1129077272](https://jq.qq.com/?_wv=1027&k=KnbeceDU)
# uView2.0重磅发布,利剑出鞘,一统江湖
1. uView2.0已实现全面兼容nvue
2. uView2.0对1.x进行了架构重构细节和性能都有极大提升
3. 目前uView2.0为公测阶段,相关细节可能会有变动
4. 我们写了一份与1.x的对比指南详见[对比1.x](https://www.uviewui.com/components/diff1.x.html)
5. 修复input组件formatter参数缺失问题

View file

@ -1,78 +0,0 @@
<template>
<uvForm
ref="uForm"
:model="model"
:rules="rules"
:errorType="errorType"
:borderBottom="borderBottom"
:labelPosition="labelPosition"
:labelWidth="labelWidth"
:labelAlign="labelAlign"
:labelStyle="labelStyle"
:customStyle="customStyle"
>
<slot />
</uvForm>
</template>
<script>
/**
* 此组件存在的理由是在nvue下u-form被uni-app官方占用了u-form在nvue中相当于form组件
* 所以在nvue下取名为u--form内部其实还是u-form.vue只不过做一层中转
*/
import uvForm from '../u-form/u-form.vue';
import props from '../u-form/props.js'
export default {
// #ifdef MP-WEIXIN
name: 'u-form',
// #endif
// #ifndef MP-WEIXIN
name: 'u--form',
// #endif
mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
components: {
uvForm
},
created() {
this.children = []
},
methods: {
//
setRules(rules) {
this.$refs.uForm.setRules(rules)
},
validate() {
/**
* 在微信小程序中通过this.$parent拿到的父组件是u--form而不是其内嵌的u-form
* 导致在u-form组件中拿不到对应的children数组从而校验无效所以这里每次调用u-form组件中的
* 对应方法的时候在小程序中都先将u--form的children赋值给u-form中的children
*/
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.validate()
},
validateField(value, callback) {
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.validateField(value, callback)
},
resetFields() {
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.resetFields()
},
clearValidate(props) {
// #ifdef MP-WEIXIN
this.setMpData()
// #endif
return this.$refs.uForm.clearValidate(props)
},
setMpData() {
this.$refs.uForm.children = this.children
}
},
}
</script>

View file

@ -1,47 +0,0 @@
<template>
<uvImage
:src="src"
:mode="mode"
:width="width"
:height="height"
:shape="shape"
:radius="radius"
:lazyLoad="lazyLoad"
:showMenuByLongpress="showMenuByLongpress"
:loadingIcon="loadingIcon"
:errorIcon="errorIcon"
:showLoading="showLoading"
:showError="showError"
:fade="fade"
:webp="webp"
:duration="duration"
:bgColor="bgColor"
:customStyle="customStyle"
@click="$emit('click')"
@error="$emit('error')"
@load="$emit('load')"
>
<template v-slot:loading>
<slot name="loading"></slot>
</template>
<template v-slot:error>
<slot name="error"></slot>
</template>
</uvImage>
</template>
<script>
/**
* 此组件存在的理由是在nvue下u-image被uni-app官方占用了u-image在nvue中相当于image组件
* 所以在nvue下取名为u--image内部其实还是u-iamge.vue只不过做一层中转
*/
import uvImage from '../u-image/u-image.vue';
import props from '../u-image/props.js';
export default {
name: 'u--image',
mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
components: {
uvImage
},
}
</script>

View file

@ -1,72 +0,0 @@
<template>
<uvInput
:value="value"
:type="type"
:fixed="fixed"
:disabled="disabled"
:disabledColor="disabledColor"
:clearable="clearable"
:password="password"
:maxlength="maxlength"
:placeholder="placeholder"
:placeholderClass="placeholderClass"
:placeholderStyle="placeholderStyle"
:showWordLimit="showWordLimit"
:confirmType="confirmType"
:confirmHold="confirmHold"
:holdKeyboard="holdKeyboard"
:focus="focus"
:autoBlur="autoBlur"
:disableDefaultPadding="disableDefaultPadding"
:cursor="cursor"
:cursorSpacing="cursorSpacing"
:selectionStart="selectionStart"
:selectionEnd="selectionEnd"
:adjustPosition="adjustPosition"
:inputAlign="inputAlign"
:fontSize="fontSize"
:color="color"
:prefixIcon="prefixIcon"
:suffixIcon="suffixIcon"
:suffixIconStyle="suffixIconStyle"
:prefixIconStyle="prefixIconStyle"
:border="border"
:readonly="readonly"
:shape="shape"
:customStyle="customStyle"
:formatter="formatter"
@focus="$emit('focus')"
@blur="$emit('blur')"
@keyboardheightchange="$emit('keyboardheightchange')"
@change="e => $emit('change', e)"
@input="e => $emit('input', e)"
@confirm="e => $emit('confirm', e)"
@clear="$emit('clear')"
@click="$emit('click')"
>
<!-- #ifdef MP -->
<slot name="prefix"></slot>
<slot name="suffix"></slot>
<!-- #endif -->
<!-- #ifndef MP -->
<slot name="prefix" slot="prefix"></slot>
<slot name="suffix" slot="suffix"></slot>
<!-- #endif -->
</uvInput>
</template>
<script>
/**
* 此组件存在的理由是在nvue下u-input被uni-app官方占用了u-input在nvue中相当于input组件
* 所以在nvue下取名为u--input内部其实还是u-input.vue只不过做一层中转
*/
import uvInput from '../u-input/u-input.vue';
import props from '../u-input/props.js'
export default {
name: 'u--input',
mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
components: {
uvInput
},
}
</script>

View file

@ -1,44 +0,0 @@
<template>
<uvText
:type="type"
:show="show"
:text="text"
:prefixIcon="prefixIcon"
:suffixIcon="suffixIcon"
:mode="mode"
:href="href"
:format="format"
:call="call"
:openType="openType"
:bold="bold"
:block="block"
:lines="lines"
:color="color"
:decoration="decoration"
:size="size"
:iconStyle="iconStyle"
:margin="margin"
:lineHeight="lineHeight"
:align="align"
:wordWrap="wordWrap"
:customStyle="customStyle"
@click="$emit('click')"
></uvText>
</template>
<script>
/**
* 此组件存在的理由是在nvue下u-text被uni-app官方占用了u-text在nvue中相当于input组件
* 所以在nvue下取名为u--input内部其实还是u-text.vue只不过做一层中转
* 不使用v-bind="$attrs"而是分开独立写传参是因为微信小程序不支持此写法
*/
import uvText from "../u-text/u-text.vue";
import props from "../u-text/props.js";
export default {
name: "u--text",
mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
components: {
uvText,
},
};
</script>

View file

@ -1,47 +0,0 @@
<template>
<uvTextarea
:value="value"
:placeholder="placeholder"
:height="height"
:confirmType="confirmType"
:disabled="disabled"
:count="count"
:focus="focus"
:autoHeight="autoHeight"
:fixed="fixed"
:cursorSpacing="cursorSpacing"
:cursor="cursor"
:showConfirmBar="showConfirmBar"
:selectionStart="selectionStart"
:selectionEnd="selectionEnd"
:adjustPosition="adjustPosition"
:disableDefaultPadding="disableDefaultPadding"
:holdKeyboard="holdKeyboard"
:maxlength="maxlength"
:border="border"
:customStyle="customStyle"
:formatter="formatter"
@focus="e => $emit('focus')"
@blur="e => $emit('blur')"
@linechange="e => $emit('linechange', e)"
@confirm="e => $emit('confirm')"
@input="e => $emit('input', e)"
@keyboardheightchange="e => $emit('keyboardheightchange')"
></uvTextarea>
</template>
<script>
/**
* 此组件存在的理由是在nvue下u--textarea被uni-app官方占用了u-textarea在nvue中相当于textarea组件
* 所以在nvue下取名为u--textarea内部其实还是u-textarea.vue只不过做一层中转
*/
import uvTextarea from '../u-textarea/u-textarea.vue';
import props from '../u-textarea/props.js'
export default {
name: 'u--textarea',
mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
components: {
uvTextarea
},
}
</script>

View file

@ -1,54 +0,0 @@
export default {
props: {
// 操作菜单是否展示 默认false
show: {
type: Boolean,
default: uni.$u.props.actionSheet.show
},
// 标题
title: {
type: String,
default: uni.$u.props.actionSheet.title
},
// 选项上方的描述信息
description: {
type: String,
default: uni.$u.props.actionSheet.description
},
// 数据
actions: {
type: Array,
default: uni.$u.props.actionSheet.actions
},
// 取消按钮的文字,不为空时显示按钮
cancelText: {
type: String,
default: uni.$u.props.actionSheet.cancelText
},
// 点击某个菜单项时是否关闭弹窗
closeOnClickAction: {
type: Boolean,
default: uni.$u.props.actionSheet.closeOnClickAction
},
// 处理底部安全区默认true
safeAreaInsetBottom: {
type: Boolean,
default: uni.$u.props.actionSheet.safeAreaInsetBottom
},
// 小程序的打开方式
openType: {
type: String,
default: uni.$u.props.actionSheet.openType
},
// 点击遮罩是否允许关闭 (默认true)
closeOnClickOverlay: {
type: Boolean,
default: uni.$u.props.actionSheet.closeOnClickOverlay
},
// 圆角值
round: {
type: [Boolean, String, Number],
default: uni.$u.props.actionSheet.round
}
}
}

View file

@ -1,275 +0,0 @@
<template>
<u-popup
:show="show"
mode="bottom"
@close="close"
:closeOnClickOverlay="closeOnClickOverlay"
:safeAreaInsetBottom="safeAreaInsetBottom"
:round="round"
>
<view class="u-action-sheet">
<view
class="u-action-sheet__header"
v-if="title"
>
<text class="u-action-sheet__header__title u-line-1">{{title}}</text>
<view
class="u-action-sheet__header__icon-wrap"
@tap.stop="close"
>
<u-icon
name="close"
size="17"
color="#c8c9cc"
bold
></u-icon>
</view>
</view>
<text
class="u-action-sheet__description"
:style="[{
marginTop: `${title && description ? 0 : '18px'}`
}]"
v-if="description"
>{{description}}</text>
<slot>
<u-line v-if="description"></u-line>
<view class="u-action-sheet__item-wrap">
<template v-for="(item, index) in actions">
<!-- #ifdef MP -->
<button
:key="index"
class="u-reset-button"
:openType="item.openType"
@getuserinfo="onGetUserInfo"
@contact="onContact"
@getphonenumber="onGetPhoneNumber"
@error="onError"
@launchapp="onLaunchApp"
@opensetting="onOpenSetting"
:lang="lang"
:session-from="sessionFrom"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:app-parameter="appParameter"
@tap="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
>
<!-- #endif -->
<view
class="u-action-sheet__item-wrap__item"
@tap.stop="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'u-action-sheet--hover' : ''"
:hover-stay-time="150"
>
<template v-if="!item.loading">
<text
class="u-action-sheet__item-wrap__item__name"
:style="[itemStyle(index)]"
>{{ item.name }}</text>
<text
v-if="item.subname"
class="u-action-sheet__item-wrap__item__subname"
>{{ item.subname }}</text>
</template>
<u-loading-icon
v-else
custom-class="van-action-sheet__loading"
size="18"
mode="circle"
/>
</view>
<!-- #ifdef MP -->
</button>
<!-- #endif -->
<u-line v-if="index !== actions.length - 1"></u-line>
</template>
</view>
</slot>
<u-gap
bgColor="#eaeaec"
height="6"
v-if="cancelText"
></u-gap>
<view hover-class="u-action-sheet--hover">
<text
@touchmove.stop.prevent
:hover-stay-time="150"
v-if="cancelText"
class="u-action-sheet__cancel-text"
@tap="close"
>{{cancelText}}</text>
</view>
</view>
</u-popup>
</template>
<script>
import openType from '../../libs/mixin/openType'
import button from '../../libs/mixin/button'
import props from './props.js';
/**
* ActionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单供用户选择并返回结果本组件功能类似于uni的uni.showActionSheetAPI配置更加灵活所有平台都表现一致
* @tutorial https://www.uviewui.com/components/actionSheet.html
*
* @property {Boolean} show 操作菜单是否展示 默认 false
* @property {String} title 操作菜单标题
* @property {String} description 选项上方的描述信息
* @property {Array<Object>} actions 按钮的文字数组见官方文档示例
* @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮
* @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 默认 true
* @property {Boolean} safeAreaInsetBottom 处理底部安全区 默认 true
* @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting getPhoneNumber error )
* @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true )
* @property {Number|String} round 圆角值默认无圆角 (默认 0 )
* @property {String} lang 指定返回用户信息的语言zh_CN 简体中文zh_TW 繁体中文en 英文
* @property {String} sessionFrom 会话来源openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片设置此参数为 true用户进入客服会话会在右下角显示"可能要发送的小程序"提示用户点击后可以快速发送小程序消息openType="contact"时有效 默认 false
* @property {String} appParameter 打开 APP APP 传递的参数openType=launchApp 时有效
*
* @event {Function} select 点击ActionSheet列表项时触发
* @event {Function} close 点击取消按钮时触发
* @event {Function} getuserinfo 用户点击该按钮时会返回获取到的用户信息回调的 detail 数据与 wx.getUserInfo 返回的一致openType="getUserInfo"时有效
* @event {Function} contact 客服消息回调openType="contact"时有效
* @event {Function} getphonenumber 获取用户手机号回调openType="getPhoneNumber"时有效
* @event {Function} error 当使用开放能力时发生错误的回调openType="error"时有效
* @event {Function} launchapp 打开 APP 成功的回调openType="launchApp"时有效
* @event {Function} opensetting 在打开授权设置页后回调openType="openSetting"时有效
* @example <u-action-sheet :actions="list" :title="title" :show="show"></u-action-sheet>
*/
export default {
name: "u-action-sheet",
// propsmethodsmixin
mixins: [openType, button, uni.$u.mixin, props],
data() {
return {
}
},
computed: {
//
itemStyle() {
return (index) => {
let style = {};
if (this.actions[index].color) style.color = this.actions[index].color
if (this.actions[index].fontSize) style.fontSize = uni.$u.addUnit(this.actions[index].fontSize)
//
if (this.actions[index].disabled) style.color = '#c0c4cc'
return style;
}
},
},
methods: {
close() {
// close
if(this.closeOnClickOverlay) {
this.$emit('close')
}
},
selectHandler(index) {
const item = this.actions[index]
if (item && !item.disabled && !item.loading) {
this.$emit('select', item)
if (this.closeOnClickAction) {
this.$emit('close')
}
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
$u-action-sheet-reset-button-width:100% !default;
$u-action-sheet-title-font-size: 16px !default;
$u-action-sheet-title-padding: 12px 30px !default;
$u-action-sheet-title-color: $u-main-color !default;
$u-action-sheet-header-icon-wrap-right:15px !default;
$u-action-sheet-header-icon-wrap-top:15px !default;
$u-action-sheet-description-font-size:13px !default;
$u-action-sheet-description-color:14px !default;
$u-action-sheet-description-margin: 18px 15px !default;
$u-action-sheet-item-wrap-item-padding:15px !default;
$u-action-sheet-item-wrap-name-font-size:16px !default;
$u-action-sheet-item-wrap-subname-font-size:13px !default;
$u-action-sheet-item-wrap-subname-color: #c0c4cc !default;
$u-action-sheet-item-wrap-subname-margin-top:10px !default;
$u-action-sheet-cancel-text-font-size:16px !default;
$u-action-sheet-cancel-text-color:$u-content-color !default;
$u-action-sheet-cancel-text-font-size:15px !default;
$u-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
.u-reset-button {
width: $u-action-sheet-reset-button-width;
}
.u-action-sheet {
text-align: center;
&__header {
position: relative;
padding: $u-action-sheet-title-padding;
&__title {
font-size: $u-action-sheet-title-font-size;
color: $u-action-sheet-title-color;
font-weight: bold;
text-align: center;
}
&__icon-wrap {
position: absolute;
right: $u-action-sheet-header-icon-wrap-right;
top: $u-action-sheet-header-icon-wrap-top;
}
}
&__description {
font-size: $u-action-sheet-description-font-size;
color: $u-tips-color;
margin: $u-action-sheet-description-margin;
text-align: center;
}
&__item-wrap {
&__item {
padding: $u-action-sheet-item-wrap-item-padding;
@include flex;
align-items: center;
justify-content: center;
flex-direction: column;
&__name {
font-size: $u-action-sheet-item-wrap-name-font-size;
color: $u-main-color;
text-align: center;
}
&__subname {
font-size: $u-action-sheet-item-wrap-subname-font-size;
color: $u-action-sheet-item-wrap-subname-color;
margin-top: $u-action-sheet-item-wrap-subname-margin-top;
text-align: center;
}
}
}
&__cancel-text {
font-size: $u-action-sheet-cancel-text-font-size;
color: $u-action-sheet-cancel-text-color;
text-align: center;
padding: $u-action-sheet-cancel-text-font-size;
}
&--hover {
background-color: $u-action-sheet-cancel-text-hover-background-color;
}
}
</style>

View file

@ -1,59 +0,0 @@
export default {
props: {
// 图片地址Array<String>|Array<Object>形式
urls: {
type: Array,
default: uni.$u.props.album.urls
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: uni.$u.props.album.keyName
},
// 单图时,图片长边的长度
singleSize: {
type: [String, Number],
default: uni.$u.props.album.singleSize
},
// 多图时,图片边长
multipleSize: {
type: [String, Number],
default: uni.$u.props.album.multipleSize
},
// 多图时,图片水平和垂直之间的间隔
space: {
type: [String, Number],
default: uni.$u.props.album.space
},
// 单图时,图片缩放裁剪的模式
singleMode: {
type: String,
default: uni.$u.props.album.singleMode
},
// 多图时,图片缩放裁剪的模式
multipleMode: {
type: String,
default: uni.$u.props.album.multipleMode
},
// 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
maxCount: {
type: [String, Number],
default: uni.$u.props.album.maxCount
},
// 是否可以预览图片
previewFullImage: {
type: Boolean,
default: uni.$u.props.album.previewFullImage
},
// 每行展示图片数量如设置singleSize和multipleSize将会无效
rowCount: {
type: [String, Number],
default: uni.$u.props.album.rowCount
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: uni.$u.props.album.showMore
}
}
}

View file

@ -1,259 +0,0 @@
<template>
<view class="u-album">
<view
class="u-album__row"
ref="u-album__row"
v-for="(arr, index) in showUrls"
:forComputedUse="albumWidth"
:key="index"
>
<view
class="u-album__row__wrapper"
v-for="(item, index1) in arr"
:key="index1"
:style="[imageStyle(index + 1, index1 + 1)]"
@tap="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
>
<image
:src="getSrc(item)"
:mode="
urls.length === 1
? imageHeight > 0
? singleMode
: 'widthFix'
: multipleMode
"
:style="[
{
width: imageWidth,
height: imageHeight
}
]"
></image>
<view
v-if="
showMore &&
urls.length > rowCount * showUrls.length &&
index === showUrls.length - 1 &&
index1 === showUrls[showUrls.length - 1].length - 1
"
class="u-album__row__wrapper__text"
>
<u--text
:text="`+${urls.length - maxCount}`"
color="#fff"
:size="multipleSize * 0.3"
align="center"
customStyle="justify-content: center"
></u--text>
</view>
</view>
</view>
</view>
</template>
<script>
import props from './props.js'
// #ifdef APP-NVUE
// weexKPIdom
const dom = uni.requireNativePlugin('dom')
// #endif
/**
* Album 相册
* @description 本组件提供一个类似相册的功能让开发者开发起来更加得心应手减少重复的模板代码
* @tutorial https://www.uviewui.com/components/album.html
*
* @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} singleSize 单图时图片长边的长度 默认 180
* @property {String | Number} multipleSize 多图时图片边长 默认 70
* @property {String | Number} space 多图时图片水平和垂直之间的间隔 默认 6
* @property {String} singleMode 单图时图片缩放裁剪的模式 默认 'scaleToFill'
* @property {String} multipleMode 多图时图片缩放裁剪的模式 默认 'aspectFill'
* @property {String | Number} maxCount 取消按钮的提示文字 默认 9
* @property {Boolean} previewFullImage 是否可以预览图片 默认 true
* @property {String | Number} rowCount 每行展示图片数量如设置singleSize和multipleSize将会无效 默认 3
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 默认 true
*
* @event {Function} albumWidth 某些特殊的情况下需要让文字与相册的宽度相等这里事件的形式对外发送 回调参数 width
* @example <u-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></u-album>
*/
export default {
name: 'u-album',
mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
data() {
return {
//
singleWidth: 0,
//
singleHeight: 0,
//
singlePercent: 0.6
}
},
watch: {
urls: {
immediate: true,
handler(newVal) {
if (newVal.length === 1) {
this.getImageRect()
}
}
}
},
computed: {
imageStyle() {
return (index1, index2) => {
const { space, rowCount, multipleSize, urls } = this,
{ addUnit, addStyle } = uni.$u,
rowLen = this.showUrls.length,
allLen = this.urls.length
const style = {
marginRight: addUnit(space),
marginBottom: addUnit(space)
}
//
if (index1 === rowLen) style.marginBottom = 0
//
if (
index2 === rowCount ||
(index1 === rowLen &&
index2 === this.showUrls[index1 - 1].length)
)
style.marginRight = 0
return style
}
},
//
showUrls() {
const arr = []
this.urls.map((item, index) => {
//
if (index + 1 <= this.maxCount) {
//
const itemIndex = Math.floor(index / this.rowCount)
//
if (!arr[itemIndex]) {
arr[itemIndex] = []
}
arr[itemIndex].push(item)
}
})
return arr
},
imageWidth() {
return uni.$u.addUnit(
this.urls.length === 1 ? this.singleWidth : this.multipleSize
)
},
imageHeight() {
return uni.$u.addUnit(
this.urls.length === 1 ? this.singleHeight : this.multipleSize
)
},
// computedurls
//
albumWidth() {
let width = 0
if (this.urls.length === 1) {
width = this.singleWidth
} else {
width =
this.showUrls[0].length * this.multipleSize +
this.space * (this.showUrls[0].length - 1)
}
this.$emit('albumWidth', width)
return width
}
},
methods: {
//
onPreviewTap(url) {
const urls = this.urls.map((item) => {
return this.getSrc(item)
})
uni.previewImage({
current: url,
urls
})
},
//
getSrc(item) {
return uni.$u.test.object(item)
? (this.keyName && item[this.keyName]) || item.src
: item
},
//
// download
// (singlePercent)
getImageRect() {
const src = this.getSrc(this.urls[0])
uni.getImageInfo({
src,
success: (res) => {
//
const isHorizotal = res.width >= res.height
this.singleWidth = isHorizotal
? this.singleSize
: (res.width / res.height) * this.singleSize
this.singleHeight = !isHorizotal
? this.singleSize
: (res.height / res.width) * this.singleWidth
},
fail: () => {
this.getComponentWidth()
}
})
},
//
async getComponentWidth() {
// dom
await uni.$u.sleep(30)
// #ifndef APP-NVUE
this.$uGetRect('.u-album__row').then((size) => {
this.singleWidth = size.width * this.singlePercent
})
// #endif
// #ifdef APP-NVUE
// ref="u-album__row"forthis.$refs['u-album__row']
const ref = this.$refs['u-album__row'][0]
ref &&
dom.getComponentRect(ref, (res) => {
this.singleWidth = res.size.width * this.singlePercent
})
// #endif
}
}
}
</script>
<style lang="scss" scoped>
@import '../../libs/css/components.scss';
.u-album {
@include flex(column);
&__row {
@include flex(row);
flex-wrap: wrap;
&__wrapper {
position: relative;
&__text {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex(row);
justify-content: center;
align-items: center;
}
}
}
}
</style>

View file

@ -1,44 +0,0 @@
export default {
props: {
// 显示文字
title: {
type: String,
default: uni.$u.props.alert.title
},
// 主题success/warning/info/error
type: {
type: String,
default: uni.$u.props.alert.type
},
// 辅助性文字
description: {
type: String,
default: uni.$u.props.alert.description
},
// 是否可关闭
closable: {
type: Boolean,
default: uni.$u.props.alert.closable
},
// 是否显示图标
showIcon: {
type: Boolean,
default: uni.$u.props.alert.showIcon
},
// 浅或深色调light-浅色dark-深色
effect: {
type: String,
default: uni.$u.props.alert.effect
},
// 文字是否居中
center: {
type: Boolean,
default: uni.$u.props.alert.center
},
// 字体大小
fontSize: {
type: [String, Number],
default: uni.$u.props.alert.fontSize
}
}
}

View file

@ -1,243 +0,0 @@
<template>
<u-transition
mode="fade"
:show="show"
>
<view
class="u-alert"
:class="[`u-alert--${type}--${effect}`]"
@tap.stop="clickHandler"
:style="[$u.addStyle(customStyle)]"
>
<view
class="u-alert__icon"
v-if="showIcon"
>
<u-icon
:name="iconName"
size="18"
:color="iconColor"
></u-icon>
</view>
<view
class="u-alert__content"
:style="[{
paddingRight: closable ? '20px' : 0
}]"
>
<text
class="u-alert__content__title"
v-if="title"
:style="[{
fontSize: $u.addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
>{{ title }}</text>
<text
class="u-alert__content__desc"
v-if="description"
:style="[{
fontSize: $u.addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
>{{ description }}</text>
</view>
<view
class="u-alert__close"
v-if="closable"
@tap.stop="closeHandler"
>
<u-icon
name="close"
:color="iconColor"
size="15"
></u-icon>
</view>
</view>
</u-transition>
</template>
<script>
import props from './props.js';
/**
* Alert 警告提示
* @description 警告提示展现需要关注的信息
* @tutorial https://www.uviewui.com/components/alertTips.html
*
* @property {String} title 显示的文字
* @property {String} type 使用预设的颜色 默认 'warning'
* @property {String} description 辅助性文字颜色比title浅一点字号也小一点可选
* @property {Boolean} closable 关闭按钮(默认为叉号icon图标) 默认 false
* @property {Boolean} showIcon 是否显示左边的辅助图标 默认 false
* @property {String} effect 多图时图片缩放裁剪的模式 默认 'light'
* @property {Boolean} center 文字是否居中 默认 false
* @property {String | Number} fontSize 字体大小 默认 14
* @property {Object} customStyle 定义需要用到的外部样式
* @event {Function} click 点击组件时触发
* @example <u-alert :title="title" type = "warning" :closable="closable" :description = "description"></u-alert>
*/
export default {
name: 'u-alert',
mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
data() {
return {
show: true
}
},
computed: {
iconColor() {
return this.effect === 'light' ? this.type : '#fff'
},
//
iconName() {
switch (this.type) {
case 'success':
return 'checkmark-circle-fill';
break;
case 'error':
return 'close-circle-fill';
break;
case 'warning':
return 'error-circle-fill';
break;
case 'info':
return 'info-circle-fill';
break;
case 'primary':
return 'more-circle-fill';
break;
default:
return 'error-circle-fill';
}
}
},
methods: {
//
clickHandler() {
this.$emit('click')
},
//
closeHandler() {
this.show = false
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-alert {
position: relative;
background-color: $u-primary;
padding: 8px 10px;
@include flex(row);
align-items: center;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
&--primary--dark {
background-color: $u-primary;
}
&--primary--light {
background-color: #ecf5ff;
}
&--error--dark {
background-color: $u-error;
}
&--error--light {
background-color: #FEF0F0;
}
&--success--dark {
background-color: $u-success;
}
&--success--light {
background-color: #f5fff0;
}
&--warning--dark {
background-color: $u-warning;
}
&--warning--light {
background-color: #FDF6EC;
}
&--info--dark {
background-color: $u-info;
}
&--info--light {
background-color: #f4f4f5;
}
&__icon {
margin-right: 5px;
}
&__content {
@include flex(column);
flex: 1;
&__title {
color: $u-main-color;
font-size: 14px;
font-weight: bold;
color: #fff;
margin-bottom: 2px;
}
&__desc {
color: $u-main-color;
font-size: 14px;
flex-wrap: wrap;
color: #fff;
}
}
&__title--dark,
&__desc--dark {
color: #FFFFFF;
}
&__text--primary--light,
&__text--primary--light {
color: $u-primary;
}
&__text--success--light,
&__text--success--light {
color: $u-success;
}
&__text--warning--light,
&__text--warning--light {
color: $u-warning;
}
&__text--error--light,
&__text--error--light {
color: $u-error;
}
&__text--info--light,
&__text--info--light {
color: $u-info;
}
&__close {
position: absolute;
top: 11px;
right: 10px;
}
}
</style>

View file

@ -1,52 +0,0 @@
export default {
props: {
// 头像图片组
urls: {
type: Array,
default: uni.$u.props.avatarGroup.urls
},
// 最多展示的头像数量
maxCount: {
type: [String, Number],
default: uni.$u.props.avatarGroup.maxCount
},
// 头像形状
shape: {
type: String,
default: uni.$u.props.avatarGroup.shape
},
// 图片裁剪模式
mode: {
type: String,
default: uni.$u.props.avatarGroup.mode
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: uni.$u.props.avatarGroup.showMore
},
// 头像大小
size: {
type: [String, Number],
default: uni.$u.props.avatarGroup.size
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: uni.$u.props.avatarGroup.keyName
},
// 头像之间的遮挡比例
gap: {
type: [String, Number],
validator(value) {
return value >= 0 && value <= 1
},
default: uni.$u.props.avatarGroup.gap
},
// 需额外显示的值
extraValue: {
type: [Number, String],
default: uni.$u.props.avatarGroup.extraValue
}
}
}

View file

@ -1,103 +0,0 @@
<template>
<view class="u-avatar-group">
<view
class="u-avatar-group__item"
v-for="(item, index) in showUrl"
:key="index"
:style="{
marginLeft: index === 0 ? 0 : $u.addUnit(-size * gap)
}"
>
<u-avatar
:size="size"
:shape="shape"
:mode="mode"
:src="$u.test.object(item) ? keyName && item[keyName] || item.url : item"
></u-avatar>
<view
class="u-avatar-group__item__show-more"
v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
@tap="clickHandler"
>
<u--text
color="#ffffff"
:size="size * 0.4"
:text="`+${extraValue || urls.length - showUrl.length}`"
align="center"
customStyle="justify-content: center"
></u--text>
</view>
</view>
</view>
</template>
<script>
import props from './props.js';
/**
* AvatarGroup 头像组
* @description 本组件一般用于展示头像的地方如个人中心或者评论列表页的用户头像展示等场所
* @tutorial https://www.uviewui.com/components/avatar.html
*
* @property {Array} urls 头像图片组 默认 []
* @property {String | Number} maxCount 最多展示的头像数量 默认 5
* @property {String} shape 头像形状 'circle' (默认) | 'square'
* @property {String} mode 图片裁剪模式默认 'scaleToFill'
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 默认 true
* @property {String | Number} size 头像大小 默认 40
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} gap 头像之间的遮挡比例0.4代表遮挡40% 默认 0.5
* @property {String | Number} extraValue 需额外显示的值
* @event {Function} showMore 头像组更多点击
* @example <u-avatar-group:urls="urls" size="35" gap="0.4" ></u-avatar-group:urls=>
*/
export default {
name: 'u-avatar-group',
mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
data() {
return {
}
},
computed: {
showUrl() {
return this.urls.slice(0, this.maxCount)
}
},
methods: {
clickHandler() {
this.$emit('showMore')
}
},
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-avatar-group {
@include flex;
&__item {
margin-left: -10px;
position: relative;
&--no-indent {
// 使:first-childnvue
margin-left: 0;
}
&__show-more {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex;
align-items: center;
justify-content: center;
border-radius: 100px;
}
}
}
</style>

View file

@ -1,78 +0,0 @@
export default {
props: {
// 头像图片路径(不能为相对路径)
src: {
type: String,
default: uni.$u.props.avatar.src
},
// 头像形状circle-圆形square-方形
shape: {
type: String,
default: uni.$u.props.avatar.shape
},
// 头像尺寸
size: {
type: [String, Number],
default: uni.$u.props.avatar.size
},
// 裁剪模式
mode: {
type: String,
default: uni.$u.props.avatar.mode
},
// 显示的文字
text: {
type: String,
default: uni.$u.props.avatar.text
},
// 背景色
bgColor: {
type: String,
default: uni.$u.props.avatar.bgColor
},
// 文字颜色
color: {
type: String,
default: uni.$u.props.avatar.color
},
// 文字大小
fontSize: {
type: [String, Number],
default: uni.$u.props.avatar.fontSize
},
// 显示的图标
icon: {
type: String,
default: uni.$u.props.avatar.icon
},
// 显示小程序头像只对百度微信QQ小程序有效
mpAvatar: {
type: Boolean,
default: uni.$u.props.avatar.mpAvatar
},
// 是否使用随机背景色
randomBgColor: {
type: Boolean,
default: uni.$u.props.avatar.randomBgColor
},
// 加载失败的默认头像(组件有内置默认图片)
defaultUrl: {
type: String,
default: uni.$u.props.avatar.defaultUrl
},
// 如果配置了randomBgColor为true且配置了此值则从默认的背景色数组中取出对应索引的颜色值取值0-19之间
colorIndex: {
type: [String, Number],
// 校验参数规则索引在0-19之间
validator(n) {
return uni.$u.test.range(n, [0, 19]) || n === ''
},
default: uni.$u.props.avatar.colorIndex
},
// 组件标识符
name: {
type: String,
default: uni.$u.props.avatar.name
}
}
}

View file

@ -1,172 +0,0 @@
<template>
<view
class="u-avatar"
:class="[`u-avatar--${shape}`]"
:style="[{
backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $u.random(0, 19)] : bgColor) : 'transparent',
width: $u.addUnit(size),
height: $u.addUnit(size),
}, $u.addStyle(customStyle)]"
@tap="clickHandler"
>
<slot>
<!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU -->
<open-data
v-if="mpAvatar && allowMp"
type="userAvatarUrl"
:style="[{
width: $u.addUnit(size),
height: $u.addUnit(size)
}]"
/>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
<template v-if="mpAvatar && allowMp"></template>
<!-- #endif -->
<u-icon
v-else-if="icon"
:name="icon"
:size="fontSize"
:color="color"
></u-icon>
<u--text
v-else-if="text"
:text="text"
:size="fontSize"
:color="color"
align="center"
customStyle="justify-content: center"
></u--text>
<image
class="u-avatar__image"
v-else
:class="[`u-avatar__image--${shape}`]"
:src="avatarUrl || defaultUrl"
:mode="mode"
@error="errorHandler"
:style="[{
width: $u.addUnit(size),
height: $u.addUnit(size)
}]"
></image>
</slot>
</view>
</template>
<script>
import props from './props.js';
const base64Avatar =
"data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
/**
* Avatar 头像
* @description 本组件一般用于展示头像的地方如个人中心或者评论列表页的用户头像展示等场所
* @tutorial https://www.uviewui.com/components/avatar.html
*
* @property {String} src 头像路径如加载失败将会显示默认头像(不能为相对路径)
* @property {String} shape 头像形状 circle (默认) | square
* @property {String | Number} size 头像尺寸可以为指定字符串(large, default, mini)或者数值 默认 40
* @property {String} mode 头像图片的裁剪类型与uni的image组件的mode参数一致如效果达不到需求可尝试传widthFix值 默认 'scaleToFill'
* @property {String} text 用文字替代图片级别优先于src
* @property {String} bgColor 背景颜色一般显示文字时用 默认 '#c0c4cc'
* @property {String} color 文字颜色 默认 '#ffffff'
* @property {String | Number} fontSize 文字大小 默认 18
* @property {String} icon 显示的图标
* @property {Boolean} mpAvatar 显示小程序头像只对百度微信QQ小程序有效 默认 false
* @property {Boolean} randomBgColor 是否使用随机背景色 默认 false
* @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片)
* @property {String | Number} colorIndex 如果配置了randomBgColor为true且配置了此值则从默认的背景色数组中取出对应索引的颜色值取值0-19之间
* @property {String} name 组件标识符 默认 'level'
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} click 点击组件时触发 index: 用户传递的标识符
* @example <u-avatar :src="src" mode="square"></u-avatar>
*/
export default {
name: 'u-avatar',
mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
data() {
return {
// randomBgColortrue
colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2',
'#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee',
'#73d1f1',
'#80a7dc'
],
avatarUrl: this.src,
allowMp: false
}
},
watch: {
// srcavatarUrlsrc
// props
src: {
immediate: true,
handler(newVal) {
this.avatarUrl = newVal
// srcerrorsrc''
if(!newVal) {
this.errorHandler()
}
}
}
},
computed: {
imageStyle() {
const style = {}
return style
}
},
created() {
this.init()
},
methods: {
init() {
// open-data
// uni.getUserInfo()
//
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
this.allowMp = true
// #endif
},
// name"/"
isImg() {
return this.src.indexOf('/') !== -1
},
//
errorHandler() {
this.avatarUrl = this.defaultUrl || base64Avatar
},
clickHandler() {
this.$emit('click', this.name)
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-avatar {
@include flex;
align-items: center;
justify-content: center;
&--circle {
border-radius: 100px;
}
&--square {
border-radius: 4px;
}
&__image {
&--circle {
border-radius: 100px;
}
&--square {
border-radius: 4px;
}
}
}
</style>

View file

@ -1,54 +0,0 @@
export default {
props: {
// 返回顶部的形状circle-圆形square-方形
mode: {
type: String,
default: uni.$u.props.backtop.mode
},
// 自定义图标
icon: {
type: String,
default: uni.$u.props.backtop.icon
},
// 提示文字
text: {
type: String,
default: uni.$u.props.backtop.text
},
// 返回顶部滚动时间
duration: {
type: [String, Number],
default: uni.$u.props.backtop.duration
},
// 滚动距离
scrollTop: {
type: [String, Number],
default: uni.$u.props.backtop.scrollTop
},
// 距离顶部多少距离显示单位px
top: {
type: [String, Number],
default: uni.$u.props.backtop.top
},
// 返回顶部按钮到底部的距离单位px
bottom: {
type: [String, Number],
default: uni.$u.props.backtop.bottom
},
// 返回顶部按钮到右边的距离单位px
right: {
type: [String, Number],
default: uni.$u.props.backtop.right
},
// 层级
zIndex: {
type: [String, Number],
default: uni.$u.props.backtop.zIndex
},
// 图标的样式,对象形式
iconStyle: {
type: Object,
default: uni.$u.props.backtop.iconStyle
}
}
}

View file

@ -1,129 +0,0 @@
<template>
<u-transition
mode="fade"
:customStyle="backTopStyle"
:show="show"
>
<view
class="u-back-top"
:style="[contentStyle]"
v-if="!$slots.default && !$slots.$default"
@click="backToTop"
>
<u-icon
:name="icon"
:custom-style="iconStyle"
></u-icon>
<text
v-if="text"
class="u-back-top__text"
>{{text}}</text>
</view>
<slot v-else />
</u-transition>
</template>
<script>
import props from './props.js';
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* backTop 返回顶部
* @description 本组件一个用于长页面滑动一定距离后出现返回顶部按钮方便快速返回顶部的场景
* @tutorial https://uviewui.com/components/backTop.html
*
* @property {String} mode 返回顶部的形状circle-圆形square-方形 默认 'circle'
* @property {String} icon 自定义图标 默认 'arrow-upward' 见官方文档示例
* @property {String} text 提示文字
* @property {String | Number} duration 返回顶部滚动时间 默认 100
* @property {String | Number} scrollTop 滚动距离 默认 0
* @property {String | Number} top 距离顶部多少距离显示单位px 默认 400
* @property {String | Number} bottom 返回顶部按钮到底部的距离单位px 默认 100
* @property {String | Number} right 返回顶部按钮到右边的距离单位px 默认 20
* @property {String | Number} zIndex 层级 默认 9
* @property {Object<Object>} iconStyle 图标的样式对象形式 默认 {color: '#909399',fontSize: '19px'}
* @property {Object} customStyle 定义需要用到的外部样式
*
* @example <u-back-top :scrollTop="scrollTop"></u-back-top>
*/
export default {
name: 'u-back-top',
mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
computed: {
backTopStyle() {
//
const style = {
bottom: uni.$u.addUnit(this.bottom),
right: uni.$u.addUnit(this.right),
width: '40px',
height: '40px',
position: 'fixed',
zIndex: 10,
}
return style
},
show() {
return uni.$u.getPx(this.scrollTop) > uni.$u.getPx(this.top)
},
contentStyle() {
const style = {}
let radius = 0
//
if(this.mode === 'circle') {
radius = '100px'
} else {
radius = '4px'
}
// nvue
style.borderTopLeftRadius = radius
style.borderTopRightRadius = radius
style.borderBottomLeftRadius = radius
style.borderBottomRightRadius = radius
return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
}
},
methods: {
backToTop() {
// #ifdef APP-NVUE
if (!this.$parent.$refs['u-back-top']) {
uni.$u.error(`nvue页面需要给页面最外层元素设置"ref='u-back-top'`)
}
dom.scrollToElement(this.$parent.$refs['u-back-top'], {
offset: 0
})
// #endif
// #ifndef APP-NVUE
uni.pageScrollTo({
scrollTop: 0,
duration: this.duration
});
// #endif
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
@import '../../libs/css/components.scss';
$u-back-top-flex:1 !default;
$u-back-top-height:100% !default;
$u-back-top-background-color:#E1E1E1 !default;
$u-back-top-tips-font-size:12px !default;
.u-back-top {
@include flex;
flex-direction: column;
align-items: center;
flex:$u-back-top-flex;
height: $u-back-top-height;
justify-content: center;
background-color: $u-back-top-background-color;
&__tips {
font-size:$u-back-top-tips-font-size;
transform: scale(0.8);
}
}
</style>

View file

@ -1,72 +0,0 @@
export default {
props: {
// 是否显示圆点
isDot: {
type: Boolean,
default: uni.$u.props.badge.isDot
},
// 显示的内容
value: {
type: [Number, String],
default: uni.$u.props.badge.value
},
// 是否显示
show: {
type: Boolean,
default: uni.$u.props.badge.show
},
// 最大值,超过最大值会显示 '{max}+'
max: {
type: [Number, String],
default: uni.$u.props.badge.max
},
// 主题类型error|warning|success|primary
type: {
type: String,
default: uni.$u.props.badge.type
},
// 当数值为 0 时,是否展示 Badge
showZero: {
type: Boolean,
default: uni.$u.props.badge.showZero
},
// 背景颜色优先级比type高如设置type参数会失效
bgColor: {
type: [String, null],
default: uni.$u.props.badge.bgColor
},
// 字体颜色
color: {
type: [String, null],
default: uni.$u.props.badge.color
},
// 徽标形状circle-四角均为圆角horn-左下角为直角
shape: {
type: String,
default: uni.$u.props.badge.shape
},
// 设置数字的显示方式overflow|ellipsis|limit
// overflow会根据max字段判断超出显示`${max}+`
// ellipsis会根据max判断超出显示`${max}...`
// limit会依据1000作为判断条件超出1000显示`${value/1000}K`比如2.2k、3.34w最多保留2位小数
numberType: {
type: String,
default: uni.$u.props.badge.numberType
},
// 设置badge的位置偏移格式为 [x, y]也即设置的为top和right的值absolute为true时有效
offset: {
type: Array,
default: uni.$u.props.badge.offset
},
// 是否反转背景和字体颜色
inverted: {
type: Boolean,
default: uni.$u.props.badge.inverted
},
// 是否绝对定位
absolute: {
type: Boolean,
default: uni.$u.props.badge.absolute
}
}
}

View file

@ -1,171 +0,0 @@
<template>
<text
v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
:class="[isDot ? 'u-badge--dot' : 'u-badge--not-dot', inverted && 'u-badge--inverted', shape === 'horn' && 'u-badge--horn', `u-badge--${type}${inverted ? '--inverted' : ''}`]"
:style="[$u.addStyle(customStyle), badgeStyle]"
class="u-badge"
>{{ isDot ? '' :showValue }}</text>
</template>
<script>
import props from './props.js';
/**
* badge 徽标数
* @description 该组件一般用于图标右上角显示未读的消息数量提示用户点击有圆点和圆包含文字两种形式
* @tutorial https://uviewui.com/components/badge.html
*
* @property {Boolean} isDot 是否显示圆点 默认 false
* @property {String | Number} value 显示的内容
* @property {Boolean} show 是否显示 默认 true
* @property {String | Number} max 最大值超过最大值会显示 '{max}+' 默认999
* @property {String} type 主题类型error|warning|success|primary 默认 'error'
* @property {Boolean} showZero 当数值为 0 是否展示 Badge 默认 false
* @property {String} bgColor 背景颜色优先级比type高如设置type参数会失效
* @property {String} color 字体颜色 默认 '#ffffff'
* @property {String} shape 徽标形状circle-四角均为圆角horn-左下角为直角 默认 'circle'
* @property {String} numberType 设置数字的显示方式overflow|ellipsis|limit 默认 'overflow'
* @property {Array}} offset 设置badge的位置偏移格式为 [x, y]也即设置的为top和right的值absolute为true时有效
* @property {Boolean} inverted 是否反转背景和字体颜色默认 false
* @property {Boolean} absolute 是否绝对定位默认 false
* @property {Object} customStyle 定义需要用到的外部样式
* @example <u-badge :type="type" :count="count"></u-badge>
*/
export default {
name: 'u-badge',
mixins: [uni.$u.mpMixin, props, uni.$u.mixin],
computed: {
// badge
boxStyle() {
let style = {};
return style;
},
//
badgeStyle() {
const style = {}
if(this.color) {
style.color = this.color
}
if (this.bgColor && !this.inverted) {
style.backgroundColor = this.bgColor
}
if (this.absolute) {
style.position = 'absolute'
// offset
if(this.offset.length) {
// toprightoffsetrighttop
const top = this.offset[0]
const right = this.offset[1] || top
style.top = uni.$u.addUnit(top)
style.right = uni.$u.addUnit(right)
}
}
return style
},
showValue() {
switch (this.numberType) {
case "overflow":
return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
break;
case "ellipsis":
return Number(this.value) > Number(this.max) ? "..." : this.value
break;
case "limit":
return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
1e3 * 100) / 100 + "k" : this.value
break;
default:
return Number(this.value)
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
$u-badge-primary: $u-primary !default;
$u-badge-error: $u-error !default;
$u-badge-success: $u-success !default;
$u-badge-info: $u-info !default;
$u-badge-warning: $u-warning !default;
$u-badge-dot-radius: 100px !default;
$u-badge-dot-size: 8px !default;
$u-badge-dot-right: 4px !default;
$u-badge-dot-top: 0 !default;
$u-badge-text-font-size: 11px !default;
$u-badge-text-right: 10px !default;
$u-badge-text-padding: 2px 5px !default;
$u-badge-text-align: center !default;
$u-badge-text-color: #FFFFFF !default;
.u-badge {
border-top-right-radius: $u-badge-dot-radius;
border-top-left-radius: $u-badge-dot-radius;
border-bottom-left-radius: $u-badge-dot-radius;
border-bottom-right-radius: $u-badge-dot-radius;
@include flex;
line-height: $u-badge-text-font-size;
text-align: $u-badge-text-align;
font-size: $u-badge-text-font-size;
color: $u-badge-text-color;
&--dot {
height: $u-badge-dot-size;
width: $u-badge-dot-size;
}
&--inverted {
font-size: 13px;
}
&--not-dot {
padding: $u-badge-text-padding;
}
&--horn {
border-bottom-left-radius: 0;
}
&--primary {
background-color: $u-badge-primary;
}
&--primary--inverted {
color: $u-badge-primary;
}
&--error {
background-color: $u-badge-error;
}
&--error--inverted {
color: $u-badge-error;
}
&--success {
background-color: $u-badge-success;
}
&--success--inverted {
color: $u-badge-success;
}
&--info {
background-color: $u-badge-info;
}
&--info--inverted {
color: $u-badge-info;
}
&--warning {
background-color: $u-badge-warning;
}
&--warning--inverted {
color: $u-badge-warning;
}
}
</style>

View file

@ -1,46 +0,0 @@
$u-button-active-opacity:0.75 !default;
$u-button-loading-text-margin-left:4px !default;
$u-button-text-color: #FFFFFF !default;
$u-button-text-plain-error-color:$u-error !default;
$u-button-text-plain-warning-color:$u-warning !default;
$u-button-text-plain-success-color:$u-success !default;
$u-button-text-plain-info-color:$u-info !default;
$u-button-text-plain-primary-color:$u-primary !default;
.u-button {
&--active {
opacity: $u-button-active-opacity;
}
&--active--plain {
background-color: rgb(217, 217, 217);
}
&__loading-text {
margin-left:$u-button-loading-text-margin-left;
}
&__text,
&__loading-text {
color:$u-button-text-color;
}
&__text--plain--error {
color:$u-button-text-plain-error-color;
}
&__text--plain--warning {
color:$u-button-text-plain-warning-color;
}
&__text--plain--success{
color:$u-button-text-plain-success-color;
}
&__text--plain--info {
color:$u-button-text-plain-info-color;
}
&__text--plain--primary {
color:$u-button-text-plain-primary-color;
}
}

View file

@ -1,161 +0,0 @@
/*
* @Author : LQ
* @Description :
* @version : 1.0
* @Date : 2021-08-16 10:04:04
* @LastAuthor : LQ
* @lastTime : 2021-08-16 10:04:24
* @FilePath : /u-view2.0/uview-ui/components/u-button/props.js
*/
export default {
props: {
// 是否细边框
hairline: {
type: Boolean,
default: uni.$u.props.button.hairline
},
// 按钮的预置样式infoprimaryerrorwarningsuccess
type: {
type: String,
default: uni.$u.props.button.type
},
// 按钮尺寸largenormalsmallmini
size: {
type: String,
default: uni.$u.props.button.size
},
// 按钮形状circle两边为半圆square带圆角
shape: {
type: String,
default: uni.$u.props.button.shape
},
// 按钮是否镂空
plain: {
type: Boolean,
default: uni.$u.props.button.plain
},
// 是否禁止状态
disabled: {
type: Boolean,
default: uni.$u.props.button.disabled
},
// 是否加载中
loading: {
type: Boolean,
default: uni.$u.props.button.loading
},
// 加载中提示文字
loadingText: {
type: [String, Number],
default: uni.$u.props.button.loadingText
},
// 加载状态图标类型
loadingMode: {
type: String,
default: uni.$u.props.button.loadingMode
},
// 加载图标大小
loadingSize: {
type: [String, Number],
default: uni.$u.props.button.loadingSize
},
// 开放能力具体请看uniapp稳定关于button组件部分说明
// https://uniapp.dcloud.io/component/button
openType: {
type: String,
default: uni.$u.props.button.openType
},
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
// 取值为submit提交表单reset重置表单
formType: {
type: String,
default: uni.$u.props.button.formType
},
// 打开 APP 时,向 APP 传递的参数open-type=launchApp时有效
// 只微信小程序、QQ小程序有效
appParameter: {
type: String,
default: uni.$u.props.button.appParameter
},
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
hoverStopPropagation: {
type: Boolean,
default: uni.$u.props.button.hoverStopPropagation
},
// 指定返回用户信息的语言zh_CN 简体中文zh_TW 繁体中文en 英文。只微信小程序有效
lang: {
type: String,
default: uni.$u.props.button.lang
},
// 会话来源open-type="contact"时有效。只微信小程序有效
sessionFrom: {
type: String,
default: uni.$u.props.button.sessionFrom
},
// 会话内消息卡片标题open-type="contact"时有效
// 默认当前标题,只微信小程序有效
sendMessageTitle: {
type: String,
default: uni.$u.props.button.sendMessageTitle
},
// 会话内消息卡片点击跳转小程序路径open-type="contact"时有效
// 默认当前分享路径,只微信小程序有效
sendMessagePath: {
type: String,
default: uni.$u.props.button.sendMessagePath
},
// 会话内消息卡片图片open-type="contact"时有效
// 默认当前页面截图,只微信小程序有效
sendMessageImg: {
type: String,
default: uni.$u.props.button.sendMessageImg
},
// 是否显示会话内消息卡片,设置此参数为 true用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
// 用户点击后可以快速发送小程序消息open-type="contact"时有效
showMessageCard: {
type: Boolean,
default: uni.$u.props.button.showMessageCard
},
// 额外传参参数用于小程序的data-xxx属性通过target.dataset.name获取
dataName: {
type: String,
default: uni.$u.props.button.dataName
},
// 节流,一定时间内只能触发一次
throttleTime: {
type: [String, Number],
default: uni.$u.props.button.throttleTime
},
// 按住后多久出现点击态,单位毫秒
hoverStartTime: {
type: [String, Number],
default: uni.$u.props.button.hoverStartTime
},
// 手指松开后点击态保留时间,单位毫秒
hoverStayTime: {
type: [String, Number],
default: uni.$u.props.button.hoverStayTime
},
// 按钮文字之所以通过props传入是因为slot传入的话
// nvue中无法控制文字的样式
text: {
type: [String, Number],
default: uni.$u.props.button.text
},
// 按钮图标
icon: {
type: String,
default: uni.$u.props.button.icon
},
// 按钮图标
iconColor: {
type: String,
default: uni.$u.props.button.icon
},
// 按钮颜色支持传入linear-gradient渐变色
color: {
type: String,
default: uni.$u.props.button.color
}
}
}

View file

@ -1,490 +0,0 @@
<template>
<!-- #ifndef APP-NVUE -->
<button
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:lang="lang"
:data-name="dataName"
:session-from="sessionFrom"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
@getphonenumber="getphonenumber"
@getuserinfo="getuserinfo"
@error="error"
@opensetting="opensetting"
@launchapp="launchapp"
:hover-class="!disabled && !loading ? 'u-button--active' : ''"
class="u-button u-reset-button"
:style="[baseColor, $u.addStyle(customStyle)]"
@tap="clickHandler"
:class="bemClass"
>
<template v-if="loading">
<u-loading-icon
:mode="loadingMode"
:size="textSize * 1.15"
:color="loadingColor"
></u-loading-icon>
<text
class="u-button__loading-text"
:style="[{ fontSize: textSize + 'px' }]"
>{{ loadingText || text }}</text
>
</template>
<template v-else>
<u-icon
v-if="icon"
:name="icon"
:color="iconColorCom"
:size="textSize * 1.35"
:customStyle="{ marginRight: '2px' }"
></u-icon>
<slot>
<text
class="u-button__text"
:style="[{ fontSize: textSize + 'px' }]"
>{{ text }}</text
>
</slot>
</template>
</button>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
class="u-button"
:hover-class="
!disabled && !loading && !color && (plain || type === 'info')
? 'u-button--active--plain'
: !disabled && !loading && !plain
? 'u-button--active'
: ''
"
@tap="clickHandler"
:class="bemClass"
:style="[baseColor, $u.addStyle(customStyle)]"
>
<template v-if="loading">
<u-loading-icon
:mode="loadingMode"
:size="textSize * 1.15"
:color="loadingColor"
></u-loading-icon>
<text
class="u-button__loading-text"
:style="[nvueTextStyle]"
:class="[plain && `u-button__text--plain--${type}`]"
>{{ loadingText || text }}</text
>
</template>
<template v-else>
<u-icon
v-if="icon"
:name="icon"
:color="iconColorCom"
:size="textSize * 1.35"
></u-icon>
<text
class="u-button__text"
:style="[
{
marginLeft: icon ? '2px' : 0,
},
nvueTextStyle,
]"
:class="[plain && `u-button__text--plain--${type}`]"
>{{ text }}</text
>
</template>
</view>
<!-- #endif -->
</template>
<script>
import button from "../../libs/mixin/button.js";
import openType from "../../libs/mixin/openType.js";
import props from "./props.js";
/**
* button 按钮
* @description Button 按钮
* @tutorial https://www.uviewui.com/components/button.html
*
* @property {Boolean} hairline 是否显示按钮的细边框 (默认 true )
* @property {String} type 按钮的预置样式infoprimaryerrorwarningsuccess (默认 'info' )
* @property {String} size 按钮尺寸largenormalmini 默认 normal
* @property {String} shape 按钮形状circle两边为半圆square带圆角 默认 'square'
* @property {Boolean} plain 按钮是否镂空背景色透明 默认 false
* @property {Boolean} disabled 是否禁用 默认 false
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台 ios 上为雪花Android上为圆圈) 默认 false
* @property {String | Number} loadingText 加载中提示文字
* @property {String} loadingMode 加载状态图标类型 默认 'spinner'
* @property {String | Number} loadingSize 加载图标大小 默认 15
* @property {String} openType 开放能力具体请看uniapp稳定关于button组件部分说明
* @property {String} formType 用于 <form> 组件点击分别会触发 <form> 组件的 submit/reset 事件
* @property {String} appParameter 打开 APP APP 传递的参数open-type=launchApp时有效 只微信小程序QQ小程序有效
* @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态微信小程序有效默认 true
* @property {String} lang 指定返回用户信息的语言zh_CN 简体中文zh_TW 繁体中文en 英文默认 en
* @property {String} sessionFrom 会话来源openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片设置此参数为 true用户进入客服会话会在右下角显示"可能要发送的小程序"提示用户点击后可以快速发送小程序消息openType="contact"时有效默认false
* @property {String} dataName 额外传参参数用于小程序的data-xxx属性通过target.dataset.name获取
* @property {String | Number} throttleTime 节流一定时间内只能触发一次 默认 0 )
* @property {String | Number} hoverStartTime 按住后多久出现点击态单位毫秒 默认 0 )
* @property {String | Number} hoverStayTime 手指松开后点击态保留时间单位毫秒 默认 200 )
* @property {String | Number} text 按钮文字之所以通过props传入是因为slot传入的话nvue中无法控制文字的样式
* @property {String} icon 按钮图标
* @property {String} iconColor 按钮图标颜色
* @property {String} color 按钮颜色支持传入linear-gradient渐变色
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} click 非禁止并且非加载中才能点击
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效
* @event {Function} getuserinfo 用户点击该按钮时会返回获取到的用户信息从返回参数的detail中获取到的值同uni.getUserInfo
* @event {Function} error 当使用开放能力时发生错误的回调
* @event {Function} opensetting 在打开授权设置页并关闭后回调
* @event {Function} launchapp 打开 APP 成功的回调
* @example <u-button>月落</u-button>
*/
export default {
name: "u-button",
// #ifdef MP
mixins: [uni.$u.mpMixin, uni.$u.mixin, button, openType, props],
// #endif
// #ifndef MP
mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
// #endif
data() {
return {};
},
computed: {
// bem
bemClass() {
// this.bemcomputedmixin
if (!this.color) {
return this.bem(
"button",
["type", "shape", "size"],
["disabled", "plain", "hairline"]
);
} else {
// nvuecolortypetype
return this.bem(
"button",
["shape", "size"],
["disabled", "plain", "hairline"]
);
}
},
loadingColor() {
if (this.plain) {
// colorcolor使type
return this.color
? this.color
: uni.$u.config.color[`u-${this.type}`];
}
if (this.type === "info") {
return "#c9c9c9";
}
return "rgb(200, 200, 200)";
},
iconColorCom() {
// colorcolor使
// u-iconcolor
if (this.iconColor) return this.iconColor;
if (this.plain) {
return this.color ? this.color : this.type;
} else {
return this.type === "info" ? "#000000" : "#ffffff";
}
},
baseColor() {
let style = {};
if (this.color) {
// color
style.color = this.plain ? this.color : "white";
if (!this.plain) {
// 使
style["background-color"] = this.color;
}
if (this.color.indexOf("gradient") !== -1) {
// backgroundImage
// weexborderWidth
// weex西
style.borderTopWidth = 0;
style.borderRightWidth = 0;
style.borderBottomWidth = 0;
style.borderLeftWidth = 0;
if (!this.plain) {
style.backgroundImage = this.color;
}
} else {
//
style.borderColor = this.color;
style.borderWidth = "1px";
style.borderStyle = "solid";
}
}
return style;
},
// nvuetext
nvueTextStyle() {
let style = {};
// color
if (this.type === "info") {
style.color = "#323233";
}
if (this.color) {
style.color = this.plain ? this.color : "white";
}
style.fontSize = this.textSize + "px";
return style;
},
//
textSize() {
let fontSize = 14,
{ size } = this;
if (size === "large") fontSize = 16;
if (size === "normal") fontSize = 14;
if (size === "small") fontSize = 12;
if (size === "mini") fontSize = 10;
return fontSize;
},
},
methods: {
clickHandler() {
//
if (!this.disabled && !this.loading) {
// this.throttle
uni.$u.throttle(() => {
this.$emit("click");
}, this.throttleTime);
}
},
// uniapp
getphonenumber(res) {
this.$emit("getphonenumber", res);
},
getuserinfo(res) {
this.$emit("getuserinfo", res);
},
error(res) {
this.$emit("error", res);
},
opensetting(res) {
this.$emit("opensetting", res);
},
launchapp(res) {
this.$emit("launchapp", res);
},
},
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
/* #ifndef APP-NVUE */
@import "./vue.scss";
/* #endif */
/* #ifdef APP-NVUE */
@import "./nvue.scss";
/* #endif */
$u-button-u-button-height: 40px !default;
$u-button-text-font-size: 15px !default;
$u-button-loading-text-font-size: 15px !default;
$u-button-loading-text-margin-left: 4px !default;
$u-button-large-width: 100% !default;
$u-button-large-height: 50px !default;
$u-button-normal-padding: 0 12px !default;
$u-button-large-padding: 0 15px !default;
$u-button-normal-font-size: 14px !default;
$u-button-small-min-width: 60px !default;
$u-button-small-height: 30px !default;
$u-button-small-padding: 0px 8px !default;
$u-button-mini-padding: 0px 8px !default;
$u-button-small-font-size: 12px !default;
$u-button-mini-height: 22px !default;
$u-button-mini-font-size: 10px !default;
$u-button-mini-min-width: 50px !default;
$u-button-disabled-opacity: 0.5 !default;
$u-button-info-color: #323233 !default;
$u-button-info-background-color: #fff !default;
$u-button-info-border-color: #ebedf0 !default;
$u-button-info-border-width: 1px !default;
$u-button-info-border-style: solid !default;
$u-button-success-color: #fff !default;
$u-button-success-background-color: $u-success !default;
$u-button-success-border-color: $u-button-success-background-color !default;
$u-button-success-border-width: 1px !default;
$u-button-success-border-style: solid !default;
$u-button-primary-color: #fff !default;
$u-button-primary-background-color: $u-primary !default;
$u-button-primary-border-color: $u-button-primary-background-color !default;
$u-button-primary-border-width: 1px !default;
$u-button-primary-border-style: solid !default;
$u-button-error-color: #fff !default;
$u-button-error-background-color: $u-error !default;
$u-button-error-border-color: $u-button-error-background-color !default;
$u-button-error-border-width: 1px !default;
$u-button-error-border-style: solid !default;
$u-button-warning-color: #fff !default;
$u-button-warning-background-color: $u-warning !default;
$u-button-warning-border-color: $u-button-warning-background-color !default;
$u-button-warning-border-width: 1px !default;
$u-button-warning-border-style: solid !default;
$u-button-block-width: 100% !default;
$u-button-circle-border-top-right-radius: 100px !default;
$u-button-circle-border-top-left-radius: 100px !default;
$u-button-circle-border-bottom-left-radius: 100px !default;
$u-button-circle-border-bottom-right-radius: 100px !default;
$u-button-square-border-top-right-radius: 3px !default;
$u-button-square-border-top-left-radius: 3px !default;
$u-button-square-border-bottom-left-radius: 3px !default;
$u-button-square-border-bottom-right-radius: 3px !default;
$u-button-icon-min-width: 1em !default;
$u-button-plain-background-color: #fff !default;
$u-button-hairline-border-width: 0.5px !default;
.u-button {
height: $u-button-u-button-height;
position: relative;
align-items: center;
justify-content: center;
@include flex;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
flex-direction: row;
&__text {
font-size: $u-button-text-font-size;
}
&__loading-text {
font-size: $u-button-loading-text-font-size;
margin-left: $u-button-loading-text-margin-left;
}
&--large {
/* #ifndef APP-NVUE */
width: $u-button-large-width;
/* #endif */
height: $u-button-large-height;
padding: $u-button-large-padding;
}
&--normal {
padding: $u-button-normal-padding;
font-size: $u-button-normal-font-size;
}
&--small {
/* #ifndef APP-NVUE */
min-width: $u-button-small-min-width;
/* #endif */
height: $u-button-small-height;
padding: $u-button-small-padding;
font-size: $u-button-small-font-size;
}
&--mini {
height: $u-button-mini-height;
font-size: $u-button-mini-font-size;
/* #ifndef APP-NVUE */
min-width: $u-button-mini-min-width;
/* #endif */
padding: $u-button-mini-padding;
}
&--disabled {
opacity: $u-button-disabled-opacity;
}
&--info {
color: $u-button-info-color;
background-color: $u-button-info-background-color;
border-color: $u-button-info-border-color;
border-width: $u-button-info-border-width;
border-style: $u-button-info-border-style;
}
&--success {
color: $u-button-success-color;
background-color: $u-button-success-background-color;
border-color: $u-button-success-border-color;
border-width: $u-button-success-border-width;
border-style: $u-button-success-border-style;
}
&--primary {
color: $u-button-primary-color;
background-color: $u-button-primary-background-color;
border-color: $u-button-primary-border-color;
border-width: $u-button-primary-border-width;
border-style: $u-button-primary-border-style;
}
&--error {
color: $u-button-error-color;
background-color: $u-button-error-background-color;
border-color: $u-button-error-border-color;
border-width: $u-button-error-border-width;
border-style: $u-button-error-border-style;
}
&--warning {
color: $u-button-warning-color;
background-color: $u-button-warning-background-color;
border-color: $u-button-warning-border-color;
border-width: $u-button-warning-border-width;
border-style: $u-button-warning-border-style;
}
&--block {
@include flex;
width: $u-button-block-width;
}
&--circle {
border-top-right-radius: $u-button-circle-border-top-right-radius;
border-top-left-radius: $u-button-circle-border-top-left-radius;
border-bottom-left-radius: $u-button-circle-border-bottom-left-radius;
border-bottom-right-radius: $u-button-circle-border-bottom-right-radius;
}
&--square {
border-bottom-left-radius: $u-button-square-border-top-right-radius;
border-bottom-right-radius: $u-button-square-border-top-left-radius;
border-top-left-radius: $u-button-square-border-bottom-left-radius;
border-top-right-radius: $u-button-square-border-bottom-right-radius;
}
&__icon {
/* #ifndef APP-NVUE */
min-width: $u-button-icon-min-width;
line-height: inherit !important;
vertical-align: top;
/* #endif */
}
&--plain {
background-color: $u-button-plain-background-color;
}
&--hairline {
border-width: $u-button-hairline-border-width !important;
}
}
</style>

View file

@ -1,80 +0,0 @@
// nvue下hover-class无效
$u-button-before-top:50% !default;
$u-button-before-left:50% !default;
$u-button-before-width:100% !default;
$u-button-before-height:100% !default;
$u-button-before-transform:translate(-50%, -50%) !default;
$u-button-before-opacity:0 !default;
$u-button-before-background-color:#000 !default;
$u-button-before-border-color:#000 !default;
$u-button-active-before-opacity:.15 !default;
$u-button-icon-margin-left:4px !default;
$u-button-plain-u-button-info-color:$u-info;
$u-button-plain-u-button-success-color:$u-success;
$u-button-plain-u-button-error-color:$u-error;
$u-button-plain-u-button-warning-color:$u-error;
.u-button {
width: 100%;
&__text {
white-space: nowrap;
line-height: 1;
}
&:before {
position: absolute;
top:$u-button-before-top;
left:$u-button-before-left;
width:$u-button-before-width;
height:$u-button-before-height;
border: inherit;
border-radius: inherit;
transform:$u-button-before-transform;
opacity:$u-button-before-opacity;
content: " ";
background-color:$u-button-before-background-color;
border-color:$u-button-before-border-color;
}
&--active {
&:before {
opacity: .15
}
}
&__icon+&__text:not(:empty),
&__loading-text {
margin-left:$u-button-icon-margin-left;
}
&--plain {
&.u-button--primary {
color: $u-primary;
}
}
&--plain {
&.u-button--info {
color:$u-button-plain-u-button-info-color;
}
}
&--plain {
&.u-button--success {
color:$u-button-plain-u-button-success-color;
}
}
&--plain {
&.u-button--error {
color:$u-button-plain-u-button-error-color;
}
}
&--plain {
&.u-button--warning {
color:$u-button-plain-u-button-warning-color;
}
}
}

View file

@ -1,99 +0,0 @@
<template>
<view class="u-calendar-header u-border-bottom">
<text
class="u-calendar-header__title"
v-if="showTitle"
>{{ title }}</text>
<text
class="u-calendar-header__subtitle"
v-if="showSubtitle"
>{{ subtitle }}</text>
<view class="u-calendar-header__weekdays">
<text class="u-calendar-header__weekdays__weekday"></text>
<text class="u-calendar-header__weekdays__weekday"></text>
<text class="u-calendar-header__weekdays__weekday"></text>
<text class="u-calendar-header__weekdays__weekday"></text>
<text class="u-calendar-header__weekdays__weekday"></text>
<text class="u-calendar-header__weekdays__weekday"></text>
<text class="u-calendar-header__weekdays__weekday"></text>
</view>
</view>
</template>
<script>
export default {
name: 'u-calendar-header',
mixins: [uni.$u.mpMixin, uni.$u.mixin],
props: {
//
title: {
type: String,
default: ''
},
//
subtitle: {
type: String,
default: ''
},
//
showTitle: {
type: Boolean,
default: true
},
//
showSubtitle: {
type: Boolean,
default: true
},
},
data() {
return {
}
},
methods: {
name() {
}
},
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-calendar-header {
padding-bottom: 4px;
&__title {
font-size: 16px;
color: $u-main-color;
text-align: center;
height: 42px;
line-height: 42px;
font-weight: bold;
}
&__subtitle {
font-size: 14px;
color: $u-main-color;
height: 40px;
text-align: center;
line-height: 40px;
font-weight: bold;
}
&__weekdays {
@include flex;
justify-content: space-between;
&__weekday {
font-size: 13px;
color: $u-main-color;
line-height: 30px;
flex: 1;
text-align: center;
}
}
}
</style>

View file

@ -1,579 +0,0 @@
<template>
<view class="u-calendar-month-wrapper" ref="u-calendar-month-wrapper">
<view v-for="(item, index) in months" :key="index" :class="[`u-calendar-month-${index}`]"
:ref="`u-calendar-month-${index}`" :id="`month-${index}`">
<text v-if="index !== 0" class="u-calendar-month__title">{{ item.year }}{{ item.month }}</text>
<view class="u-calendar-month__days">
<view v-if="showMark" class="u-calendar-month__days__month-mark-wrapper">
<text class="u-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text>
</view>
<view class="u-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1"
:style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)"
:class="[item1.selected && 'u-calendar-month__days__day__select--selected']">
<view class="u-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]">
<text class="u-calendar-month__days__day__select__info"
:class="[item1.disabled && 'u-calendar-month__days__day__select__info--disabled']"
:style="[textStyle(item1)]">{{ item1.day }}</text>
<text v-if="getBottomInfo(index, index1, item1)"
class="u-calendar-month__days__day__select__buttom-info"
:class="[item1.disabled && 'u-calendar-month__days__day__select__buttom-info--disabled']"
:style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text>
<text v-if="item1.dot" class="u-calendar-month__days__day__select__dot"></text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
// nvue
const dom = uni.requireNativePlugin('dom')
// #endif
import dayjs from '../../libs/util/dayjs.js';
export default {
name: 'u-calendar-month',
mixins: [uni.$u.mpMixin, uni.$u.mixin],
props: {
//
showMark: {
type: Boolean,
default: true
},
//
color: {
type: String,
default: '#3c9cff'
},
//
months: {
type: Array,
default: () => []
},
//
mode: {
type: String,
default: 'single'
},
//
rowHeight: {
type: [String, Number],
default: 58
},
// mode=multiple
maxCount: {
type: [String, Number],
default: Infinity
},
// mode=range
startText: {
type: String,
default: '开始'
},
// mode=range
endText: {
type: String,
default: '结束'
},
// modemultiplerange
defaultDate: {
type: [Array, String, Date],
default: null
},
//
minDate: {
type: [String, Number],
default: 0
},
//
maxDate: {
type: [String, Number],
default: 0
},
// maxDate
maxMonth: {
type: [String, Number],
default: 2
},
//
readonly: {
type: Boolean,
default: uni.$u.props.calendar.readonly
},
// mode = range
maxRange: {
type: [Number, String],
default: Infinity
},
// mode = range
rangePrompt: {
type: String,
default: ''
},
// mode = range
showRangePrompt: {
type: Boolean,
default: true
},
// mode = range
allowSameDay: {
type: Boolean,
default: false
}
},
data() {
return {
//
width: 0,
// item
item: {},
selected: []
}
},
watch: {
selectedChange: {
immediate: true,
handler(n) {
this.setDefaultDate()
}
}
},
computed: {
//
selectedChange() {
return [this.minDate, this.maxDate, this.defaultDate]
},
dayStyle(index1, index2, item) {
return (index1, index2, item) => {
const style = {}
let week = item.week
// 2
const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1))
//
// #ifdef APP-NVUE
style.width = uni.$u.addUnit(dayWidth)
// #endif
style.height = uni.$u.addUnit(this.rowHeight)
if (index2 === 0) {
// 0item
week = (week === 0 ? 7 : week) - 1
style.marginLeft = uni.$u.addUnit(week * dayWidth)
}
if (this.mode === 'range') {
// DCloudiOSbug
style.paddingLeft = 0
style.paddingRight = 0
style.paddingBottom = 0
style.paddingTop = 0
}
return style
}
},
daySelectStyle() {
return (index1, index2, item) => {
let date = dayjs(item.date).format("YYYY-MM-DD"),
style = {}
// dateselected0使dateSameincludes
if (this.selected.some(item => this.dateSame(item, date))) {
style.backgroundColor = this.color
}
if (this.mode === 'single') {
if (date === this.selected[0]) {
// nvue
style.borderTopLeftRadius = '3px'
style.borderBottomLeftRadius = '3px'
style.borderTopRightRadius = '3px'
style.borderBottomRightRadius = '3px'
}
} else if (this.mode === 'range') {
if (this.selected.length >= 2) {
const len = this.selected.length - 1
//
if (this.dateSame(date, this.selected[0])) {
style.borderTopLeftRadius = '3px'
style.borderBottomLeftRadius = '3px'
}
//
if (this.dateSame(date, this.selected[len])) {
style.borderTopRightRadius = '3px'
style.borderBottomRightRadius = '3px'
}
//
if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
.selected[len]))) {
style.backgroundColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[90]
// mark
style.opacity = 0.7
}
} else if (this.selected.length === 1) {
// DCloudiOSbug
// nvueiOSuni-appbug
style.borderTopLeftRadius = '3px'
style.borderBottomLeftRadius = '3px'
}
} else {
if (this.selected.some(item => this.dateSame(item, date))) {
style.borderTopLeftRadius = '3px'
style.borderBottomLeftRadius = '3px'
style.borderTopRightRadius = '3px'
style.borderBottomRightRadius = '3px'
}
}
return style
}
},
//
textStyle() {
return (item) => {
const date = dayjs(item.date).format("YYYY-MM-DD"),
style = {}
//
if (this.selected.some(item => this.dateSame(item, date))) {
style.color = '#ffffff'
}
if (this.mode === 'range') {
const len = this.selected.length - 1
//
if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
.selected[len]))) {
style.color = this.color
}
}
return style
}
},
//
getBottomInfo() {
return (index1, index2, item) => {
const date = dayjs(item.date).format("YYYY-MM-DD")
const bottomInfo = item.bottomInfo
// 0
if (this.mode === 'range' && this.selected.length > 0) {
if (this.selected.length === 1) {
//
if (this.dateSame(date, this.selected[0])) return this.startText
else return bottomInfo
} else {
const len = this.selected.length - 1
// 2
if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) &&
len === 1) {
// 2item
return `${this.startText}/${this.endText}`
} else if (this.dateSame(date, this.selected[0])) {
return this.startText
} else if (this.dateSame(date, this.selected[len])) {
return this.endText
} else {
return bottomInfo
}
}
} else {
return bottomInfo
}
}
}
},
mounted() {
this.init()
},
methods: {
init() {
//
this.$emit('monthSelected', this.selected)
this.$nextTick(() => {
//
// nvue$nextTick100%
uni.$u.sleep(10).then(() => {
this.getWrapperWidth()
this.getMonthRect()
})
})
},
//
dateSame(date1, date2) {
return dayjs(date1).isSame(dayjs(date2))
},
// nvuecssitem
getWrapperWidth() {
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['u-calendar-month-wrapper'], res => {
this.width = res.size.width
})
// #endif
// #ifndef APP-NVUE
this.$uGetRect('.u-calendar-month-wrapper').then(size => {
this.width = size.width
})
// #endif
},
getMonthRect() {
// scroll-view
const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise(
`u-calendar-month-${index}`))
//
Promise.all(promiseAllArr).then(
sizes => {
let height = 1
const topArr = []
for (let i = 0; i < this.months.length; i++) {
// monthsscroll-view
topArr[i] = height
height += sizes[i].height
}
// this.months[i].top()monthtop使
this.$emit('updateMonthTop', topArr)
})
},
//
getMonthRectByPromise(el) {
// #ifndef APP-NVUE
// $uGetRectuViewhttps://www.uviewui.com/js/getRect.html
// this.$uGetRectuni.$u.getRect
return new Promise(resolve => {
this.$uGetRect(`.${el}`).then(size => {
resolve(size)
})
})
// #endif
// #ifdef APP-NVUE
// nvue使dom
// promise使then
return new Promise(resolve => {
dom.getComponentRect(this.$refs[el][0], res => {
resolve(res.size)
})
})
// #endif
},
//
clickHandler(index1, index2, item) {
if (this.readonly) {
return;
}
this.item = item
const date = dayjs(item.date).format("YYYY-MM-DD")
if (item.disabled) return
//
let selected = uni.$u.deepClone(this.selected)
if (this.mode === 'single') {
//
selected = [date]
} else if (this.mode === 'multiple') {
if (selected.some(item => this.dateSame(item, date))) {
//
const itemIndex = selected.findIndex(item => item === date)
selected.splice(itemIndex, 1)
} else {
//
if (selected.length < this.maxCount) selected.push(date)
}
} else {
//
if (selected.length === 0 || selected.length >= 2) {
// 02
selected = [date]
} else if (selected.length === 1) {
//
const existsDate = selected[0]
//
if (dayjs(date).isBefore(existsDate)) {
selected = [date]
} else if (dayjs(date).isAfter(existsDate)) {
//
if(dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) {
if(this.rangePrompt) {
uni.$u.toast(this.rangePrompt)
} else {
uni.$u.toast(`选择天数不能超过 ${this.maxRange}`)
}
return
}
//
selected.push(date)
const startDate = selected[0]
const endDate = selected[1]
const arr = []
let i = 0
do {
//
arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD"))
i++
//
} while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate)))
// computedarr
arr.push(endDate)
selected = arr
} else {
//
if (selected[0] === date && !this.allowSameDay) return
selected.push(date)
}
}
}
this.setSelected(selected)
},
//
setDefaultDate() {
if (!this.defaultDate) {
//
const selected = [dayjs().format("YYYY-MM-DD")]
return this.setSelected(selected, false)
}
let defaultDate = []
const minDate = this.minDate || dayjs().format("YYYY-MM-DD")
const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD")
if (this.mode === 'single') {
// Date
if (!uni.$u.test.array(this.defaultDate)) {
defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")]
} else {
defaultDate = [this.defaultDate[0]]
}
} else {
//
if (!uni.$u.test.array(this.defaultDate)) return
defaultDate = this.defaultDate
}
//
defaultDate = defaultDate.filter(item => {
return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs(
maxDate).add(1, 'day'))
})
this.setSelected(defaultDate, false)
},
setSelected(selected, event = true) {
this.selected = selected
event && this.$emit('monthSelected', this.selected)
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-calendar-month-wrapper {
margin-top: 4px;
}
.u-calendar-month {
&__title {
font-size: 14px;
line-height: 42px;
height: 42px;
color: $u-main-color;
text-align: center;
font-weight: bold;
}
&__days {
position: relative;
@include flex;
flex-wrap: wrap;
&__month-mark-wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
@include flex;
justify-content: center;
align-items: center;
&__text {
font-size: 155px;
color: rgba(231, 232, 234, 0.83);
}
}
&__day {
@include flex;
padding: 2px;
/* #ifndef APP-NVUE */
// vue使cssjs
width: calc(100% / 7);
box-sizing: border-box;
/* #endif */
&__select {
flex: 1;
@include flex;
align-items: center;
justify-content: center;
position: relative;
&__dot {
width: 7px;
height: 7px;
border-radius: 100px;
background-color: $u-error;
position: absolute;
top: 12px;
right: 7px;
}
&__buttom-info {
color: $u-content-color;
text-align: center;
position: absolute;
bottom: 5px;
font-size: 10px;
text-align: center;
left: 0;
right: 0;
&--selected {
color: #ffffff;
}
&--disabled {
color: #cacbcd;
}
}
&__info {
text-align: center;
font-size: 16px;
&--selected {
color: #ffffff;
}
&--disabled {
color: #cacbcd;
}
}
&--selected {
background-color: $u-primary;
@include flex;
justify-content: center;
align-items: center;
flex: 1;
border-radius: 3px;
}
&--range-selected {
opacity: 0.3;
border-radius: 0;
}
&--range-start-selected {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&--range-end-selected {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
}
}
}
</style>

View file

@ -1,144 +0,0 @@
export default {
props: {
// 日历顶部标题
title: {
type: String,
default: uni.$u.props.calendar.title
},
// 是否显示标题
showTitle: {
type: Boolean,
default: uni.$u.props.calendar.showTitle
},
// 是否显示副标题
showSubtitle: {
type: Boolean,
default: uni.$u.props.calendar.showSubtitle
},
// 日期类型选择single-选择单个日期multiple-可以选择多个日期range-选择日期范围
mode: {
type: String,
default: uni.$u.props.calendar.mode
},
// mode=range时第一个日期底部的提示文字
startText: {
type: String,
default: uni.$u.props.calendar.startText
},
// mode=range时最后一个日期底部的提示文字
endText: {
type: String,
default: uni.$u.props.calendar.endText
},
// 自定义列表
customList: {
type: Array,
default: uni.$u.props.calendar.customList
},
// 主题色,对底部按钮和选中日期有效
color: {
type: String,
default: uni.$u.props.calendar.color
},
// 最小的可选日期
minDate: {
type: [String, Number],
default: uni.$u.props.calendar.minDate
},
// 最大可选日期
maxDate: {
type: [String, Number],
default: uni.$u.props.calendar.maxDate
},
// 默认选中的日期mode为multiple或range是必须为数组格式
defaultDate: {
type: [Array, String, Date, null],
default: uni.$u.props.calendar.defaultDate
},
// mode=multiple时最多可选多少个日期
maxCount: {
type: [String, Number],
default: uni.$u.props.calendar.maxCount
},
// 日期行高
rowHeight: {
type: [String, Number],
default: uni.$u.props.calendar.rowHeight
},
// 日期格式化函数
formatter: {
type: [Function, null],
default: uni.$u.props.calendar.formatter
},
// 是否显示农历
showLunar: {
type: Boolean,
default: uni.$u.props.calendar.showLunar
},
// 是否显示月份背景色
showMark: {
type: Boolean,
default: uni.$u.props.calendar.showMark
},
// 确定按钮的文字
confirmText: {
type: String,
default: uni.$u.props.calendar.confirmText
},
// 确认按钮处于禁用状态时的文字
confirmDisabledText: {
type: String,
default: uni.$u.props.calendar.confirmDisabledText
},
// 是否显示日历弹窗
show: {
type: Boolean,
default: uni.$u.props.calendar.show
},
// 是否允许点击遮罩关闭日历
closeOnClickOverlay: {
type: Boolean,
default: uni.$u.props.calendar.closeOnClickOverlay
},
// 是否为只读状态,只读状态下禁止选择日期
readonly: {
type: Boolean,
default: uni.$u.props.calendar.readonly
},
// 是否展示确认按钮
showConfirm: {
type: Boolean,
default: uni.$u.props.calendar.showConfirm
},
// 日期区间最多可选天数默认无限制mode = range时有效
maxRange: {
type: [Number, String],
default: uni.$u.props.calendar.maxRange
},
// 范围选择超过最多可选天数时的提示文案mode = range时有效
rangePrompt: {
type: String,
default: uni.$u.props.calendar.rangePrompt
},
// 范围选择超过最多可选天数时是否展示提示文案mode = range时有效
showRangePrompt: {
type: Boolean,
default: uni.$u.props.calendar.showRangePrompt
},
// 是否允许日期范围的起止时间为同一天mode = range时有效
allowSameDay: {
type: Boolean,
default: uni.$u.props.calendar.allowSameDay
},
// 圆角值
round: {
type: [Boolean, String, Number],
default: uni.$u.props.calendar.round
},
// 最多展示月份数量
monthNum: {
type: [Number, String],
default: 3
}
}
}

View file

@ -1,383 +0,0 @@
<template>
<u-popup
:show="show"
mode="bottom"
closeable
@close="close"
:round="round"
:closeOnClickOverlay="closeOnClickOverlay"
>
<view class="u-calendar">
<uHeader
:title="title"
:subtitle="subtitle"
:showSubtitle="showSubtitle"
:showTitle="showTitle"
></uHeader>
<scroll-view
:style="{
height: $u.addUnit(listHeight)
}"
scroll-y
@scroll="onScroll"
:scroll-top="scrollTop"
:scrollIntoView="scrollIntoView"
>
<uMonth
:color="color"
:rowHeight="rowHeight"
:showMark="showMark"
:months="months"
:mode="mode"
:maxCount="maxCount"
:startText="startText"
:endText="endText"
:defaultDate="defaultDate"
:minDate="innerMinDate"
:maxDate="innerMaxDate"
:maxMonth="monthNum"
:readonly="readonly"
:maxRange="maxRange"
:rangePrompt="rangePrompt"
:showRangePrompt="showRangePrompt"
:allowSameDay="allowSameDay"
ref="month"
@monthSelected="monthSelected"
@updateMonthTop="updateMonthTop"
></uMonth>
</scroll-view>
<slot name="footer" v-if="showConfirm">
<view class="u-calendar__confirm">
<u-button
shape="circle"
:text="
buttonDisabled ? confirmDisabledText : confirmText
"
:color="color"
@click="confirm"
:disabled="buttonDisabled"
></u-button>
</view>
</slot>
</view>
</u-popup>
</template>
<script>
import uHeader from './header.vue'
import uMonth from './month.vue'
import props from './props.js'
import util from './util.js'
import dayjs from '../../libs/util/dayjs.js'
import Calendar from '../../libs/util/calendar.js'
/**
* Calendar 日历
* @description 此组件用于单个选择日期范围选择日期等日历被包裹在底部弹起的容器中.
* @tutorial https://www.uviewui.com/components/calendar.html
*
* @property {String} title 标题内容 (默认 日期选择 )
* @property {Boolean} showTitle 是否显示标题 (默认 true )
* @property {Boolean} showSubtitle 是否显示副标题 (默认 true )
* @property {String} mode 日期类型选择 single-选择单个日期multiple-可以选择多个日期range-选择日期范围 默认 'single' )
* @property {String} startText mode=range时第一个日期底部的提示文字 (默认 '开始' )
* @property {String} endText mode=range时最后一个日期底部的提示文字 (默认 '结束' )
* @property {Array} customList 自定义列表
* @property {String} color 主题色对底部按钮和选中日期有效 (默认 #3c9cff' )
* @property {String | Number} minDate 最小的可选日期 (默认 0 )
* @property {String | Number} maxDate 最大可选日期 (默认 0 )
* @property {Array | String| Date} defaultDate 默认选中的日期mode为multiple或range是必须为数组格式
* @property {String | Number} maxCount mode=multiple时最多可选多少个日期 (默认 Number.MAX_SAFE_INTEGER )
* @property {String | Number} rowHeight 日期行高 (默认 56 )
* @property {Function} formatter 日期格式化函数
* @property {Boolean} showLunar 是否显示农历 (默认 false )
* @property {Boolean} showMark 是否显示月份背景色 (默认 true )
* @property {String} confirmText 确定按钮的文字 (默认 '确定' )
* @property {String} confirmDisabledText 确认按钮处于禁用状态时的文字 (默认 '确定' )
* @property {Boolean} show 是否显示日历弹窗 (默认 false )
* @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭日历 (默认 false )
* @property {Boolean} readonly 是否为只读状态只读状态下禁止选择日期 (默认 false )
* @property {String | Number} maxRange 日期区间最多可选天数默认无限制mode = range时有效
* @property {String} rangePrompt 范围选择超过最多可选天数时的提示文案mode = range时有效
* @property {Boolean} showRangePrompt 范围选择超过最多可选天数时是否展示提示文案mode = range时有效 (默认 true )
* @property {Boolean} allowSameDay 是否允许日期范围的起止时间为同一天mode = range时有效 (默认 false )
* @property {Number|String} round 圆角值默认无圆角 (默认 0 )
* @property {Number|String} monthNum 最多展示的月份数量 (默认 3 )
*
* @event {Function()} confirm 点击确定按钮时触发 选择日期相关的返回参数
* @event {Function()} close 日历关闭时触发 可定义页面关闭时的回调事件
* @example <u-calendar :defaultDate="defaultDateMultiple" :show="show" mode="multiple" @confirm="confirm">
</u-calendar>
* */
export default {
name: 'u-calendar',
mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
components: {
uHeader,
uMonth
},
data() {
return {
//
months: [],
// index
monthIndex: 0,
//
listHeight: 0,
// month
selected: [],
scrollIntoView: '',
scrollTop:0,
//
innerFormatter: (value) => value
}
},
watch: {
selectedChange: {
immediate: true,
handler(n) {
this.setMonth()
}
},
//
show: {
immediate: true,
handler(n) {
this.setMonth()
}
}
},
computed: {
// maxDateminDate(2021-10-10)()dayjs
innerMaxDate() {
return uni.$u.test.number(this.maxDate)
? Number(this.maxDate)
: this.maxDate
},
innerMinDate() {
return uni.$u.test.number(this.minDate)
? Number(this.minDate)
: this.minDate
},
//
selectedChange() {
return [this.innerMinDate, this.innerMaxDate, this.defaultDate]
},
subtitle() {
// this.months
if (this.months.length) {
return `${this.months[this.monthIndex].year}${
this.months[this.monthIndex].month
}`
} else {
return ''
}
},
buttonDisabled() {
// range1disabled
if (this.mode === 'range') {
if (this.selected.length <= 1) {
return true
} else {
return false
}
} else {
return false
}
}
},
mounted() {
this.start = Date.now()
this.init()
},
methods: {
// propsref
setFormatter(e) {
this.innerFormatter = e
},
// month
monthSelected(e) {
this.selected = e
if (!this.showConfirm) {
// 2
if (
this.mode === 'multiple' ||
this.mode === 'single' ||
(this.mode === 'range' && this.selected.length >= 2)
) {
this.$emit('confirm', this.selected)
}
}
},
init() {
// maxDate
if (
this.innerMaxDate &&
new Date(this.innerMaxDate).getTime() <= Date.now()
) {
return uni.$u.error('maxDate不能小于当前时间')
}
//
this.listHeight = this.rowHeight * 5 + 30
this.setMonth()
},
close() {
this.$emit('close')
},
//
confirm() {
if (!this.buttonDisabled) {
this.$emit('confirm', this.selected)
}
},
//
getMonths(minDate, maxDate) {
const minYear = dayjs(minDate).year()
const minMonth = dayjs(minDate).month() + 1
const maxYear = dayjs(maxDate).year()
const maxMonth = dayjs(maxDate).month() + 1
return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1
},
//
setMonth() {
//
const minDate = this.innerMinDate || dayjs().valueOf()
// 3
const maxDate =
this.innerMaxDate ||
dayjs(minDate)
.add(this.monthNum - 1, 'month')
.valueOf()
//
const months = uni.$u.range(
1,
this.monthNum,
this.getMonths(minDate, maxDate)
)
//
this.months = []
for (let i = 0; i < months; i++) {
this.months.push({
date: new Array(
dayjs(minDate).add(i, 'month').daysInMonth()
)
.fill(1)
.map((item, index) => {
// 1-31
let day = index + 1
// 0-60
const week = dayjs(minDate)
.add(i, 'month')
.date(day)
.day()
const date = dayjs(minDate)
.add(i, 'month')
.date(day)
.format('YYYY-MM-DD')
let bottomInfo = ''
if (this.showLunar) {
//
const lunar = Calendar.solar2lunar(
dayjs(date).year(),
dayjs(date).month() + 1,
dayjs(date).date()
)
bottomInfo = lunar.IDayCn
}
let config = {
day,
week,
// disabled
disabled:
dayjs(date).isBefore(
dayjs(minDate).format('YYYY-MM-DD')
) ||
dayjs(date).isAfter(
dayjs(maxDate).format('YYYY-MM-DD')
),
// formatter
date: new Date(date),
bottomInfo,
dot: false,
month:
dayjs(minDate).add(i, 'month').month() + 1
}
const formatter =
this.formatter || this.innerFormatter
return formatter(config)
}),
//
month: dayjs(minDate).add(i, 'month').month() + 1,
//
year: dayjs(minDate).add(i, 'month').year()
})
}
},
//
scrollIntoDefaultMonth(selected) {
//
const _index = this.months.findIndex(({
year,
month
}) => {
month = uni.$u.padZero(month)
return `${year}-${month}` === selected
})
if (_index !== -1) {
// #ifndef MP-WEIXIN
this.$nextTick(() => {
this.scrollIntoView = `month-${_index}`
})
// #endif
// #ifdef MP-WEIXIN
this.scrollTop = this.months[_index].top || 0;
// #endif
}
},
// scroll-view
onScroll(event) {
// 0scroll-view
const scrollTop = Math.max(0, event.detail.scrollTop)
//
for (let i = 0; i < this.months.length; i++) {
if (scrollTop >= (this.months[i].top || this.listHeight)) {
this.monthIndex = i
}
}
},
// top
updateMonthTop(topArr = []) {
// toponScroll
topArr.map((item, index) => {
this.months[index].top = item
})
//
if (!this.defaultDate) {
//
const selected = dayjs().format("YYYY-MM")
this.scrollIntoDefaultMonth(selected)
return
}
let selected = dayjs().format("YYYY-MM");
// Date
if (!uni.$u.test.array(this.defaultDate)) {
selected = dayjs(this.defaultDate).format("YYYY-MM")
} else {
selected = dayjs(this.defaultDate[0]).format("YYYY-MM");
}
this.scrollIntoDefaultMonth(selected)
}
}
}
</script>
<style lang="scss" scoped>
@import '../../libs/css/components.scss';
.u-calendar {
&__confirm {
padding: 7px 18px;
}
}
</style>

View file

@ -1,85 +0,0 @@
export default {
methods: {
// 设置月份数据
setMonth() {
// 月初是周几
const day = dayjs(this.date).date(1).day()
const start = day == 0 ? 6 : day - 1
// 本月天数
const days = dayjs(this.date).endOf('month').format('D')
// 上个月天数
const prevDays = dayjs(this.date).endOf('month').subtract(1, 'month').format('D')
// 日期数据
const arr = []
// 清空表格
this.month = []
// 添加上月数据
arr.push(
...new Array(start).fill(1).map((e, i) => {
const day = prevDays - start + i + 1
return {
value: day,
disabled: true,
date: dayjs(this.date).subtract(1, 'month').date(day).format('YYYY-MM-DD')
}
})
)
// 添加本月数据
arr.push(
...new Array(days - 0).fill(1).map((e, i) => {
const day = i + 1
return {
value: day,
date: dayjs(this.date).date(day).format('YYYY-MM-DD')
}
})
)
// 添加下个月
arr.push(
...new Array(42 - days - start).fill(1).map((e, i) => {
const day = i + 1
return {
value: day,
disabled: true,
date: dayjs(this.date).add(1, 'month').date(day).format('YYYY-MM-DD')
}
})
)
// 分割数组
for (let n = 0; n < arr.length; n += 7) {
this.month.push(
arr.slice(n, n + 7).map((e, i) => {
e.index = i + n
// 自定义信息
const custom = this.customList.find((c) => c.date == e.date)
// 农历
if (this.lunar) {
const {
IDayCn,
IMonthCn
} = this.getLunar(e.date)
e.lunar = IDayCn == '初一' ? IMonthCn : IDayCn
}
return {
...e,
...custom
}
})
)
}
}
}
}

View file

@ -1,14 +0,0 @@
export default {
props: {
// 是否打乱键盘按键的顺序
random: {
type: Boolean,
default: false
},
// 输入一个中文后,是否自动切换到英文
autoChange: {
type: Boolean,
default: false
}
}
}

View file

@ -1,311 +0,0 @@
<template>
<view
class="u-keyboard"
@touchmove.stop.prevent="noop"
>
<view
v-for="(group, i) in abc ? engKeyBoardList : areaList"
:key="i"
class="u-keyboard__button"
:index="i"
:class="[i + 1 === 4 && 'u-keyboard__button--center']"
>
<view
v-if="i === 3"
class="u-keyboard__button__inner-wrapper"
>
<view
class="u-keyboard__button__inner-wrapper__left"
hover-class="u-hover-class"
:hover-stay-time="200"
@tap="changeCarInputMode"
>
<text
class="u-keyboard__button__inner-wrapper__left__lang"
:class="[!abc && 'u-keyboard__button__inner-wrapper__left__lang--active']"
></text>
<text class="u-keyboard__button__inner-wrapper__left__line">/</text>
<text
class="u-keyboard__button__inner-wrapper__left__lang"
:class="[abc && 'u-keyboard__button__inner-wrapper__left__lang--active']"
></text>
</view>
</view>
<view
class="u-keyboard__button__inner-wrapper"
v-for="(item, j) in group"
:key="j"
>
<view
class="u-keyboard__button__inner-wrapper__inner"
:hover-stay-time="200"
@tap="carInputClick(i, j)"
hover-class="u-hover-class"
>
<text class="u-keyboard__button__inner-wrapper__inner__text">{{ item }}</text>
</view>
</view>
<view
v-if="i === 3"
@touchstart="backspaceClick"
@touchend="clearTimer"
class="u-keyboard__button__inner-wrapper"
>
<view
class="u-keyboard__button__inner-wrapper__right"
hover-class="u-hover-class"
:hover-stay-time="200"
>
<u-icon
size="28"
name="backspace"
color="#303133"
></u-icon>
</view>
</view>
</view>
</view>
</template>
<script>
import props from './props.js';
/**
* keyboard 键盘组件
* @description 此为uView自定义的键盘面板内含了数字键盘车牌号键身份证号键盘3种模式都有可以打乱按键顺序的选项
* @tutorial https://uviewui.com/components/keyboard.html
* @property {Boolean} random 是否打乱键盘的顺序
* @event {Function} change 点击键盘触发
* @event {Function} backspace 点击退格键触发
* @example <u-keyboard ref="uKeyboard" mode="car" v-model="show"></u-keyboard>
*/
export default {
name: "u-keyboard",
mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
data() {
return {
// abc=truebac=false
abc: false
};
},
computed: {
areaList() {
let data = [
'京',
'沪',
'粤',
'津',
'冀',
'豫',
'云',
'辽',
'黑',
'湘',
'皖',
'鲁',
'苏',
'浙',
'赣',
'鄂',
'桂',
'甘',
'晋',
'陕',
'蒙',
'吉',
'闽',
'贵',
'渝',
'川',
'青',
'琼',
'宁',
'挂',
'藏',
'港',
'澳',
'新',
'使',
'学'
];
let tmp = [];
//
if (this.random) data = uni.$u.randomArray(data);
//
tmp[0] = data.slice(0, 10);
tmp[1] = data.slice(10, 20);
tmp[2] = data.slice(20, 30);
tmp[3] = data.slice(30, 36);
return tmp;
},
engKeyBoardList() {
let data = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
0,
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'Z',
'X',
'C',
'V',
'B',
'N',
'M'
];
let tmp = [];
if (this.random) data = uni.$u.randomArray(data);
tmp[0] = data.slice(0, 10);
tmp[1] = data.slice(10, 20);
tmp[2] = data.slice(20, 30);
tmp[3] = data.slice(30, 36);
return tmp;
}
},
methods: {
//
carInputClick(i, j) {
let value = '';
//
if (this.abc) value = this.engKeyBoardList[i][j];
else value = this.areaList[i][j];
//
if (!this.abc && this.autoChange) uni.$u.sleep(200).then(() => this.abc = true)
this.$emit('change', value);
},
// |
changeCarInputMode() {
this.abc = !this.abc;
},
// 退
backspaceClick() {
this.$emit('backspace');
clearInterval(this.timer); //
this.timer = null;
this.timer = setInterval(() => {
this.$emit('backspace');
}, 250);
},
clearTimer() {
clearInterval(this.timer);
this.timer = null;
},
}
};
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
$u-car-keyboard-background-color: rgb(224, 228, 230) !default;
$u-car-keyboard-padding:6px 0 6px !default;
$u-car-keyboard-button-inner-width:64rpx !default;
$u-car-keyboard-button-inner-background-color:#FFFFFF !default;
$u-car-keyboard-button-height:80rpx !default;
$u-car-keyboard-button-inner-box-shadow:0 1px 0px #999992 !default;
$u-car-keyboard-button-border-radius:4px !default;
$u-car-keyboard-button-inner-margin:8rpx 5rpx !default;
$u-car-keyboard-button-text-font-size:16px !default;
$u-car-keyboard-button-text-color:$u-main-color !default;
$u-car-keyboard-center-inner-margin: 0 4rpx !default;
$u-car-keyboard-special-button-width:134rpx !default;
$u-car-keyboard-lang-font-size:16px !default;
$u-car-keyboard-lang-color:$u-main-color !default;
$u-car-keyboard-active-color:$u-primary !default;
$u-car-keyboard-line-font-size:15px !default;
$u-car-keyboard-line-color:$u-main-color !default;
$u-car-keyboard-line-margin:0 1px !default;
$u-car-keyboard-u-hover-class-background-color:#BBBCC6 !default;
.u-keyboard {
@include flex(column);
justify-content: space-around;
background-color: $u-car-keyboard-background-color;
align-items: stretch;
padding: $u-car-keyboard-padding;
&__button {
@include flex;
justify-content: center;
flex: 1;
/* #ifndef APP-NVUE */
/* #endif */
&__inner-wrapper {
box-shadow: $u-car-keyboard-button-inner-box-shadow;
margin: $u-car-keyboard-button-inner-margin;
border-radius: $u-car-keyboard-button-border-radius;
&__inner {
@include flex;
justify-content: center;
align-items: center;
width: $u-car-keyboard-button-inner-width;
background-color: $u-car-keyboard-button-inner-background-color;
height: $u-car-keyboard-button-height;
border-radius: $u-car-keyboard-button-border-radius;
&__text {
font-size: $u-car-keyboard-button-text-font-size;
color: $u-car-keyboard-button-text-color;
}
}
&__left,
&__right {
border-radius: $u-car-keyboard-button-border-radius;
width: $u-car-keyboard-special-button-width;
height: $u-car-keyboard-button-height;
background-color: $u-car-keyboard-u-hover-class-background-color;
@include flex;
justify-content: center;
align-items: center;
box-shadow: $u-car-keyboard-button-inner-box-shadow;
}
&__left {
&__line {
font-size: $u-car-keyboard-line-font-size;
color: $u-car-keyboard-line-color;
margin: $u-car-keyboard-line-margin;
}
&__lang {
font-size: $u-car-keyboard-lang-font-size;
color: $u-car-keyboard-lang-color;
&--active {
color: $u-car-keyboard-active-color;
}
}
}
}
}
}
.u-hover-class {
background-color: $u-car-keyboard-u-hover-class-background-color;
}
</style>

View file

@ -1,14 +0,0 @@
export default {
props: {
// 分组标题
title: {
type: String,
default: uni.$u.props.cellGroup.title
},
// 是否显示外边框
border: {
type: Boolean,
default: uni.$u.props.cellGroup.border
}
}
}

View file

@ -1,61 +0,0 @@
<template>
<view :style="[$u.addStyle(customStyle)]" :class="[customClass]" class="u-cell-group">
<view v-if="title" class="u-cell-group__title">
<slot name="title">
<text class="u-cell-group__title__text">{{ title }}</text>
</slot>
</view>
<view class="u-cell-group__wrapper">
<u-line v-if="border"></u-line>
<slot />
</view>
</view>
</template>
<script>
import props from './props.js';
/**
* cellGroup 单元格
* @description cell单元格一般用于一组列表的情况比如个人中心页设置页等
* @tutorial https://uviewui.com/components/cell.html
*
* @property {String} title 分组标题
* @property {Boolean} border 是否显示外边框 (默认 true )
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} click 点击cell列表时触发
* @example <u-cell-group title="设置喜好">
*/
export default {
name: 'u-cell-group',
mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
$u-cell-group-title-padding: 16px 16px 8px !default;
$u-cell-group-title-font-size: 15px !default;
$u-cell-group-title-line-height: 16px !default;
$u-cell-group-title-color: $u-main-color !default;
.u-cell-group {
flex: 1;
&__title {
padding: $u-cell-group-title-padding;
&__text {
font-size: $u-cell-group-title-font-size;
line-height: $u-cell-group-title-line-height;
color: $u-cell-group-title-color;
}
}
&__wrapper {
position: relative;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show more