zmq před 1 rokem
rodič
revize
04eeb13fc9

+ 52 - 0
src/api/admin/ProjectStat.ts

@@ -0,0 +1,52 @@
+import { AxiosResponse } from 'axios'
+import {
+    PageInputProjectStatManagePageInput,
+    ResultOutputPageOutputProjectStatManagePageOutput    
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class ProjectStatApi<SecurityDataType = unknown> extends HttpClient<SecurityDataType>{
+    /**
+   * No description
+   *
+   * @tags projectstat
+   * @name GetPage
+   * @summary 查询管理分页
+   * @request POST:/api/admin/project-stat/get-manage-page
+   * @secure
+   */
+    getPageManage = (data: PageInputProjectStatManagePageInput, params: RequestParams = {}) =>
+        this.request<ResultOutputPageOutputProjectStatManagePageOutput, any>({
+            path: `/api/admin/project-stat/get-manage-page`,
+            method: 'Post',
+            body: data,
+            secure: true,
+            type: ContentType.Json,
+            format: 'json',
+            ...params,
+        }) 
+    /**
+     * No description
+     *
+     * @tags projectstat
+     * @name Get
+     * @summary 结算
+     * @request GET:/api/admin/project-stat/update-settle
+     * @secure
+     */
+    Settle = (
+        query?: {
+            ProjectId: number,
+            EffecDate:string
+        },
+        params: RequestParams = {}
+    ) =>
+        this.request<AxiosResponse, any>({
+            path: `/api/admin/project-stat/update-settle`,
+            method: 'GET',
+            query: query,
+            secure: true,
+            format: 'json',
+            ...params,
+        })
+}

+ 71 - 0
src/api/admin/data-contracts.ts

@@ -5198,4 +5198,75 @@ export interface TenantDrawSetInput {
   drawRatio?: number
   /** 生效时间 */
   effectDate?: string|null
+}
+/** 项目统计管理分页信息输入 */
+export interface PageInputProjectStatManagePageInput {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: ProjectStatManagePageInput
+}
+export interface ProjectStatManagePageInput {
+  /**
+   * 项目Id  
+   */
+  projectId?: number |null ,
+  /**
+   * 平台Id
+   */
+  tenantId?: number | null
+  /**
+   * 结算状态  
+   */
+  isSettele?: number | null,
+  /**
+   * 作业时间  
+   */
+  effectDate?: string | null
+}
+/** 结果输出 */
+export interface ResultOutputPageOutputProjectStatManagePageOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputProjectStatManagePageOutput
+}
+export interface PageOutputProjectStatManagePageOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: ProjectStatManagePageOutput[] | null
+}
+export interface ProjectStatManagePageOutput {
+  // 公司(推广码来源)  
+  projectName: string
+  // 平台
+  orgName: string
+  // 作业时间
+  effectDate: string
+  // 有效数量
+  vaildCount: number
+  // 总数  
+  count: number
+  // 佣金
+  totalCommission: number
+  // 项目Id  
+  projectId: number
+  // 平台Id
+  tenantId: number
 }

+ 3 - 1
src/types/mitt.d.ts

@@ -26,6 +26,7 @@
  * @method refreshProjectLink 刷新推广码
  * @method refreshProjectPrice 刷新项目价格
  * @method refreshCompanyDraw 刷新公司抽成
+ * @method refreshProjectStat 刷新项目统计
  */
 declare type MittType<T = any> = {
   openSetingsDrawer?: string
@@ -54,7 +55,8 @@ declare type MittType<T = any> = {
   refreshNotice?: T
   refreshProjectLink?: T
   refreshProjectPrice?: T
-  refreshCompanyDraw?:T
+  refreshCompanyDraw?: T
+  refreshProjectStat?:T
 }
 
 // mitt 参数类型定义

+ 96 - 0
src/views/admin/article/alternotice.vue

@@ -0,0 +1,96 @@
+<template>
+    <div class="my-layout">
+        <el-card class="my-fill mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+            <el-form ref="formRef" :model="state.form" size="default" label-width="80px">
+                    <el-row :gutter="35">                      
+                      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                          <el-form-item label="内容" prop="content" :rules="[{ required: true, message: '请输入通知内容', trigger: ['blur', 'change'] }]">                
+                            <Editor v-model:get-html="state.editor.htmlVal" v-model:get-text="state.editor.textVal" :disable="state.editor.disable" />
+                          </el-form-item>
+                        </el-col>
+                    </el-row>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                        <el-form-item size="large">
+                            <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+                        </el-form-item>
+                    </el-col>                    
+                  </el-form> 
+        </el-card>        
+    </div>
+             
+</template>
+<script lang="ts" setup name="admin/notice/alternotice">
+
+// import { stat } from 'fs'
+import { reactive, getCurrentInstance, ref, defineAsyncComponent, onMounted } from 'vue'
+import { NoticeAddInput, NoticeUpdateInput } from '/@/api/admin/data-contracts'
+import { NoticeApi } from '/@/api/admin/Notice'
+
+// 引入组件
+const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'))
+
+
+const { proxy } = getCurrentInstance() as any
+
+
+const formRef = ref()
+
+const state = reactive({    
+    sureLoading: false,
+    form: {} as NoticeAddInput & NoticeUpdateInput,
+    editor: {
+        htmlVal: '',
+        textVal: '',
+        disable: false,
+    },
+})
+
+onMounted(() => {
+    onQuery();    
+
+})
+
+// 查询重要通知
+const onQuery = async () => {    
+    const res = await new NoticeApi().getWeight({ }, { loading: true }).catch(() => {
+        proxy.$modal.closeLoading()
+    })
+    
+    if (res?.success) {
+        if (res.data != null) {
+            state.form = res.data as NoticeAddInput & NoticeUpdateInput;
+            state.editor.htmlVal = state.form.content;
+        } else { 
+            state.form = {} as NoticeAddInput & NoticeUpdateInput
+        }
+        
+    }    
+}
+
+// 确定
+const onSure = () => {
+    if (state.editor.textVal!="")
+        state.form.content = state.editor.htmlVal;    
+    formRef.value.validate(async (valid: boolean) => {        
+        if (!valid) return        
+        state.sureLoading = true
+        state.form.isAlter = 1;
+        state.form.rank = 0;
+        state.form.title='重要通知'
+        let res = {} as any
+        if (state.form.id != undefined && state.form.id > 0) {
+            res = await new NoticeApi().update(state.form, { showSuccessMessage: true }).catch(() => {                
+                state.sureLoading = false
+            })
+        } else {
+            res = await new NoticeApi().add(state.form, { showSuccessMessage: true }).catch(() => {                
+                state.sureLoading = false
+            })
+        }
+        if (res?.success) {
+            onQuery();
+            state.sureLoading = false
+        }
+    }) 
+}
+</script>

+ 141 - 0
src/views/admin/article/components/notice-form.vue

@@ -0,0 +1,141 @@
+<template>
+  <div>    
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="769px"
+    >
+      <el-form ref="formRef" :model="form" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.title" autocomplete="off" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+              <el-form-item label="内容">                
+                <Editor v-model:get-html="state.editor.htmlVal" v-model:get-text="state.editor.textVal" :disable="state.editor.disable" />
+              </el-form-item>
+            </el-col>                                          
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="排序" prop="rank" placeholder="填写排序数值,越大越靠前" >
+              <el-input v-model="form.rank" autocomplete="off" />
+            </el-form-item>
+          </el-col>          
+            <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                <el-form-item label="公告标签" prop="Tags" >
+                  <el-radio-group v-model="form.tags">
+                    <el-radio label="最新" ></el-radio>
+                    <el-radio label="重要"></el-radio>
+                  </el-radio-group>
+                </el-form-item>
+              </el-col>                                        
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/notice/form">
+
+// import { stat } from 'fs'
+import { reactive, toRefs, getCurrentInstance, ref, defineAsyncComponent } from 'vue'
+import { NoticeAddInput, NoticeUpdateInput } from '/@/api/admin/data-contracts'
+import { NoticeApi } from '/@/api/admin/Notice'
+import eventBus from '/@/utils/mitt'
+// 引入组件
+const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'))
+
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+})
+
+const { proxy } = getCurrentInstance() as any
+
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,  
+  form: {} as NoticeAddInput & NoticeUpdateInput, 
+  editor: {
+    htmlVal:'',
+    textVal: '',
+    disable: false,
+  }, 
+})
+const { form } = toRefs(state)
+
+
+// 打开对话框
+const open = async (row: any = {}) => {  
+    
+  if (row.id > 0) {
+    const res = await new NoticeApi().get({ id: row.id }, { loading: true }).catch(() => {
+      proxy.$modal.closeLoading()
+    })
+
+    if (res?.success) {
+      state.form = res.data as NoticeAddInput & NoticeUpdateInput        
+      state.editor.htmlVal = state.form.content;      
+    }
+  } else {
+    state.form = {} as NoticeAddInput & NoticeUpdateInput
+    state.form.tags = '最新';
+    state.editor.htmlVal = '';
+    state.editor.textVal = '';    
+  }
+  state.showDialog = true
+}
+
+
+// 取消
+const onCancel = () => {   
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.form.content = state.editor.htmlVal;
+
+    state.sureLoading = true
+    let res = {} as any
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new NoticeApi().update(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new NoticeApi().add(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshNotice')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>

+ 121 - 0
src/views/admin/article/index.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="my-layout">
+    <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+      <el-form :inline="true" @submit.stop.prevent>
+        <el-form-item>
+          <el-button v-auth="'api:admin:notice:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-card class="my-fill mt8" shadow="never">
+      <el-table v-loading="state.loading" :data="state.tenantListData" row-key="id" height="'100%'"
+        style="width: 100%; height: 100%">
+        <el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
+        <el-table-column prop="createdTime" label="添加时间" width="180" show-overflow-tooltip />
+        <el-table-column prop="rank" label="排序" width="120" show-overflow-tooltip />
+        <el-table-column label="操作" width="140" header-align="center" align="center" fixed="right">
+          <template #default="{ row }">
+            <el-button v-auth="'api:admin:notice:update'" icon="ele-EditPen" size="small" text type="primary"
+              @click="onEdit(row)">编辑</el-button>
+              <el-button v-auth="'api:admin:notice:soft-delete'" icon="ele-Delete" size="small" text type="danger"
+                @click="onDelete(row)">删除</el-button>            
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="my-flex my-flex-end" style="margin-top: 20px">
+        <el-pagination v-model:currentPage="state.pageInput.currentPage" v-model:page-size="state.pageInput.pageSize"
+          :total="state.total" :page-sizes="[10, 20, 50, 100]" small background @size-change="onSizeChange"
+          @current-change="onCurrentChange" layout="total, sizes, prev, pager, next, jumper" />
+      </div>
+    </el-card>
+
+    <notice-form ref="noticeFormRef" :title="state.noticeFormTitle"></notice-form>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/notice/index">
+import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
+import { NoticeGetPageOutput, PageInputNoticeGetPageDto } from '/@/api/admin/data-contracts'
+import { NoticeApi } from '/@/api/admin/Notice'
+import eventBus from '/@/utils/mitt'
+import { auth } from '/@/utils/authFunction'
+
+// 引入组件
+const NoticeForm = defineAsyncComponent(() => import('./components/notice-form.vue'))
+const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
+
+const { proxy } = getCurrentInstance() as any
+
+const noticeFormRef = ref()
+
+const state = reactive({
+  loading: false,
+  noticeFormTitle: '',
+  total: 0,
+  filter: {
+
+  },
+  pageInput: {
+    currentPage: 1,
+    pageSize: 20,
+  } as PageInputNoticeGetPageDto,
+  tenantListData: [] as Array<NoticeGetPageOutput>,
+})
+
+onMounted(() => {
+  onQuery()
+  eventBus.off('refreshNotice')
+  eventBus.on('refreshNotice', async () => {
+    onQuery()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshNotice')
+})
+
+const onQuery = async () => {
+  state.loading = true
+  state.pageInput.filter = state.filter
+  const res = await new NoticeApi().getPage(state.pageInput).catch(() => {
+    state.loading = false
+  })
+
+  state.tenantListData = res?.data?.list ?? []
+  state.total = res?.data?.total ?? 0
+  state.loading = false
+}
+
+const onAdd = () => {
+  state.noticeFormTitle = '新增公告'
+  noticeFormRef.value.open()
+}
+
+const onEdit = (row: NoticeGetPageOutput) => {
+  state.noticeFormTitle = '编辑公告'
+  noticeFormRef.value.open(row)
+}
+
+const onDelete = (row: NoticeGetPageOutput) => {
+  proxy.$modal
+    .confirmDelete(`确定要删除【${row.title}】?`)
+    .then(async () => {
+      await new NoticeApi().softDelete({ id: row.id }, { loading: true, showSuccessMessage: true })
+      onQuery()
+    })
+    .catch(() => { })
+}
+
+const onSizeChange = (val: number) => {
+  state.pageInput.pageSize = val
+  onQuery()
+}
+
+const onCurrentChange = (val: number) => {
+  state.pageInput.currentPage = val
+  onQuery()
+}
+</script>

+ 96 - 0
src/views/admin/banner/alternotice.vue

@@ -0,0 +1,96 @@
+<template>
+    <div class="my-layout">
+        <el-card class="my-fill mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+            <el-form ref="formRef" :model="state.form" size="default" label-width="80px">
+                    <el-row :gutter="35">                      
+                      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                          <el-form-item label="内容" prop="content" :rules="[{ required: true, message: '请输入通知内容', trigger: ['blur', 'change'] }]">                
+                            <Editor v-model:get-html="state.editor.htmlVal" v-model:get-text="state.editor.textVal" :disable="state.editor.disable" />
+                          </el-form-item>
+                        </el-col>
+                    </el-row>
+                    <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                        <el-form-item size="large">
+                            <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+                        </el-form-item>
+                    </el-col>                    
+                  </el-form> 
+        </el-card>        
+    </div>
+             
+</template>
+<script lang="ts" setup name="admin/notice/alternotice">
+
+// import { stat } from 'fs'
+import { reactive, getCurrentInstance, ref, defineAsyncComponent, onMounted } from 'vue'
+import { NoticeAddInput, NoticeUpdateInput } from '/@/api/admin/data-contracts'
+import { NoticeApi } from '/@/api/admin/Notice'
+
+// 引入组件
+const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'))
+
+
+const { proxy } = getCurrentInstance() as any
+
+
+const formRef = ref()
+
+const state = reactive({    
+    sureLoading: false,
+    form: {} as NoticeAddInput & NoticeUpdateInput,
+    editor: {
+        htmlVal: '',
+        textVal: '',
+        disable: false,
+    },
+})
+
+onMounted(() => {
+    onQuery();    
+
+})
+
+// 查询重要通知
+const onQuery = async () => {    
+    const res = await new NoticeApi().getWeight({ }, { loading: true }).catch(() => {
+        proxy.$modal.closeLoading()
+    })
+    
+    if (res?.success) {
+        if (res.data != null) {
+            state.form = res.data as NoticeAddInput & NoticeUpdateInput;
+            state.editor.htmlVal = state.form.content;
+        } else { 
+            state.form = {} as NoticeAddInput & NoticeUpdateInput
+        }
+        
+    }    
+}
+
+// 确定
+const onSure = () => {
+    if (state.editor.textVal!="")
+        state.form.content = state.editor.htmlVal;    
+    formRef.value.validate(async (valid: boolean) => {        
+        if (!valid) return        
+        state.sureLoading = true
+        state.form.isAlter = 1;
+        state.form.rank = 0;
+        state.form.title='重要通知'
+        let res = {} as any
+        if (state.form.id != undefined && state.form.id > 0) {
+            res = await new NoticeApi().update(state.form, { showSuccessMessage: true }).catch(() => {                
+                state.sureLoading = false
+            })
+        } else {
+            res = await new NoticeApi().add(state.form, { showSuccessMessage: true }).catch(() => {                
+                state.sureLoading = false
+            })
+        }
+        if (res?.success) {
+            onQuery();
+            state.sureLoading = false
+        }
+    }) 
+}
+</script>

+ 141 - 0
src/views/admin/banner/components/notice-form.vue

@@ -0,0 +1,141 @@
+<template>
+  <div>    
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="769px"
+    >
+      <el-form ref="formRef" :model="form" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.title" autocomplete="off" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+              <el-form-item label="内容">                
+                <Editor v-model:get-html="state.editor.htmlVal" v-model:get-text="state.editor.textVal" :disable="state.editor.disable" />
+              </el-form-item>
+            </el-col>                                          
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="排序" prop="rank" placeholder="填写排序数值,越大越靠前" >
+              <el-input v-model="form.rank" autocomplete="off" />
+            </el-form-item>
+          </el-col>          
+            <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                <el-form-item label="公告标签" prop="Tags" >
+                  <el-radio-group v-model="form.tags">
+                    <el-radio label="最新" ></el-radio>
+                    <el-radio label="重要"></el-radio>
+                  </el-radio-group>
+                </el-form-item>
+              </el-col>                                        
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/notice/form">
+
+// import { stat } from 'fs'
+import { reactive, toRefs, getCurrentInstance, ref, defineAsyncComponent } from 'vue'
+import { NoticeAddInput, NoticeUpdateInput } from '/@/api/admin/data-contracts'
+import { NoticeApi } from '/@/api/admin/Notice'
+import eventBus from '/@/utils/mitt'
+// 引入组件
+const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'))
+
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+})
+
+const { proxy } = getCurrentInstance() as any
+
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,  
+  form: {} as NoticeAddInput & NoticeUpdateInput, 
+  editor: {
+    htmlVal:'',
+    textVal: '',
+    disable: false,
+  }, 
+})
+const { form } = toRefs(state)
+
+
+// 打开对话框
+const open = async (row: any = {}) => {  
+    
+  if (row.id > 0) {
+    const res = await new NoticeApi().get({ id: row.id }, { loading: true }).catch(() => {
+      proxy.$modal.closeLoading()
+    })
+
+    if (res?.success) {
+      state.form = res.data as NoticeAddInput & NoticeUpdateInput        
+      state.editor.htmlVal = state.form.content;      
+    }
+  } else {
+    state.form = {} as NoticeAddInput & NoticeUpdateInput
+    state.form.tags = '最新';
+    state.editor.htmlVal = '';
+    state.editor.textVal = '';    
+  }
+  state.showDialog = true
+}
+
+
+// 取消
+const onCancel = () => {   
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.form.content = state.editor.htmlVal;
+
+    state.sureLoading = true
+    let res = {} as any
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new NoticeApi().update(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new NoticeApi().add(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshNotice')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>

+ 121 - 0
src/views/admin/banner/index.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="my-layout">
+    <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+      <el-form :inline="true" @submit.stop.prevent>
+        <el-form-item>
+          <el-button v-auth="'api:admin:notice:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-card class="my-fill mt8" shadow="never">
+      <el-table v-loading="state.loading" :data="state.tenantListData" row-key="id" height="'100%'"
+        style="width: 100%; height: 100%">
+        <el-table-column prop="title" label="标题" min-width="120" show-overflow-tooltip />
+        <el-table-column prop="createdTime" label="添加时间" width="180" show-overflow-tooltip />
+        <el-table-column prop="rank" label="排序" width="120" show-overflow-tooltip />
+        <el-table-column label="操作" width="140" header-align="center" align="center" fixed="right">
+          <template #default="{ row }">
+            <el-button v-auth="'api:admin:notice:update'" icon="ele-EditPen" size="small" text type="primary"
+              @click="onEdit(row)">编辑</el-button>
+              <el-button v-auth="'api:admin:notice:soft-delete'" icon="ele-Delete" size="small" text type="danger"
+                @click="onDelete(row)">删除</el-button>            
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="my-flex my-flex-end" style="margin-top: 20px">
+        <el-pagination v-model:currentPage="state.pageInput.currentPage" v-model:page-size="state.pageInput.pageSize"
+          :total="state.total" :page-sizes="[10, 20, 50, 100]" small background @size-change="onSizeChange"
+          @current-change="onCurrentChange" layout="total, sizes, prev, pager, next, jumper" />
+      </div>
+    </el-card>
+
+    <notice-form ref="noticeFormRef" :title="state.noticeFormTitle"></notice-form>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/notice/index">
+import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
+import { NoticeGetPageOutput, PageInputNoticeGetPageDto } from '/@/api/admin/data-contracts'
+import { NoticeApi } from '/@/api/admin/Notice'
+import eventBus from '/@/utils/mitt'
+import { auth } from '/@/utils/authFunction'
+
+// 引入组件
+const NoticeForm = defineAsyncComponent(() => import('./components/notice-form.vue'))
+const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
+
+const { proxy } = getCurrentInstance() as any
+
+const noticeFormRef = ref()
+
+const state = reactive({
+  loading: false,
+  noticeFormTitle: '',
+  total: 0,
+  filter: {
+
+  },
+  pageInput: {
+    currentPage: 1,
+    pageSize: 20,
+  } as PageInputNoticeGetPageDto,
+  tenantListData: [] as Array<NoticeGetPageOutput>,
+})
+
+onMounted(() => {
+  onQuery()
+  eventBus.off('refreshNotice')
+  eventBus.on('refreshNotice', async () => {
+    onQuery()
+  })
+})
+
+onBeforeMount(() => {
+  eventBus.off('refreshNotice')
+})
+
+const onQuery = async () => {
+  state.loading = true
+  state.pageInput.filter = state.filter
+  const res = await new NoticeApi().getPage(state.pageInput).catch(() => {
+    state.loading = false
+  })
+
+  state.tenantListData = res?.data?.list ?? []
+  state.total = res?.data?.total ?? 0
+  state.loading = false
+}
+
+const onAdd = () => {
+  state.noticeFormTitle = '新增公告'
+  noticeFormRef.value.open()
+}
+
+const onEdit = (row: NoticeGetPageOutput) => {
+  state.noticeFormTitle = '编辑公告'
+  noticeFormRef.value.open(row)
+}
+
+const onDelete = (row: NoticeGetPageOutput) => {
+  proxy.$modal
+    .confirmDelete(`确定要删除【${row.title}】?`)
+    .then(async () => {
+      await new NoticeApi().softDelete({ id: row.id }, { loading: true, showSuccessMessage: true })
+      onQuery()
+    })
+    .catch(() => { })
+}
+
+const onSizeChange = (val: number) => {
+  state.pageInput.pageSize = val
+  onQuery()
+}
+
+const onCurrentChange = (val: number) => {
+  state.pageInput.currentPage = val
+  onQuery()
+}
+</script>

+ 141 - 0
src/views/admin/projectstat/components/notice-form.vue

@@ -0,0 +1,141 @@
+<template>
+  <div>    
+    <el-dialog
+      v-model="state.showDialog"
+      destroy-on-close
+      :title="title"
+      draggable
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      width="769px"
+    >
+      <el-form ref="formRef" :model="form" size="default" label-width="80px">
+        <el-row :gutter="35">
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="标题" prop="title" :rules="[{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }]">
+              <el-input v-model="form.title" autocomplete="off" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+              <el-form-item label="内容">                
+                <Editor v-model:get-html="state.editor.htmlVal" v-model:get-text="state.editor.textVal" :disable="state.editor.disable" />
+              </el-form-item>
+            </el-col>                                          
+          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <el-form-item label="排序" prop="rank" placeholder="填写排序数值,越大越靠前" >
+              <el-input v-model="form.rank" autocomplete="off" />
+            </el-form-item>
+          </el-col>          
+            <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                <el-form-item label="公告标签" prop="Tags" >
+                  <el-radio-group v-model="form.tags">
+                    <el-radio label="最新" ></el-radio>
+                    <el-radio label="重要"></el-radio>
+                  </el-radio-group>
+                </el-form-item>
+              </el-col>                                        
+        </el-row>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="admin/notice/form">
+
+// import { stat } from 'fs'
+import { reactive, toRefs, getCurrentInstance, ref, defineAsyncComponent } from 'vue'
+import { NoticeAddInput, NoticeUpdateInput } from '/@/api/admin/data-contracts'
+import { NoticeApi } from '/@/api/admin/Notice'
+import eventBus from '/@/utils/mitt'
+// 引入组件
+const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'))
+
+
+defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+})
+
+const { proxy } = getCurrentInstance() as any
+
+
+const formRef = ref()
+const state = reactive({
+  showDialog: false,
+  sureLoading: false,  
+  form: {} as NoticeAddInput & NoticeUpdateInput, 
+  editor: {
+    htmlVal:'',
+    textVal: '',
+    disable: false,
+  }, 
+})
+const { form } = toRefs(state)
+
+
+// 打开对话框
+const open = async (row: any = {}) => {  
+    
+  if (row.id > 0) {
+    const res = await new NoticeApi().get({ id: row.id }, { loading: true }).catch(() => {
+      proxy.$modal.closeLoading()
+    })
+
+    if (res?.success) {
+      state.form = res.data as NoticeAddInput & NoticeUpdateInput        
+      state.editor.htmlVal = state.form.content;      
+    }
+  } else {
+    state.form = {} as NoticeAddInput & NoticeUpdateInput
+    state.form.tags = '最新';
+    state.editor.htmlVal = '';
+    state.editor.textVal = '';    
+  }
+  state.showDialog = true
+}
+
+
+// 取消
+const onCancel = () => {   
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (!valid) return
+
+    state.form.content = state.editor.htmlVal;
+
+    state.sureLoading = true
+    let res = {} as any
+    if (state.form.id != undefined && state.form.id > 0) {
+      res = await new NoticeApi().update(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    } else {
+      res = await new NoticeApi().add(state.form, { showSuccessMessage: true }).catch(() => {
+        state.sureLoading = false
+      })
+    }
+    state.sureLoading = false
+
+    if (res?.success) {
+      eventBus.emit('refreshNotice')
+      state.showDialog = false
+    }
+  })
+}
+
+defineExpose({
+  open,
+})
+</script>

+ 219 - 0
src/views/admin/projectstat/index.vue

@@ -0,0 +1,219 @@
+<template>
+  <div class="my-layout">
+    <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+      <el-form :inline="true" @submit.stop.prevent>
+        <el-form-item>
+          <el-select lable="选择项目" v-model="state.uploadFilter.ProjectId" value-key="id" clearable>
+            <el-option v-for="item in state.uploadProjectList" :key="item.id" :label="item.name"
+              :value="item.id"></el-option>
+          </el-select>
+        </el-form-item>       
+        <el-form-item>
+          <el-upload style="margin-right: 5px;" :action="StatAction" :headers="StatHeaders"
+            :data="{ ProjectId: state.uploadFilter.ProjectId }"
+            :show-file-list="false" :before-upload="OnUploadStatBefore" :on-success="onUploadStatSuccess"
+            :on-error="onUploadStatError" accept=".xlsx">
+            <el-button type="primary" icon="ele-Upload"> 导入每日统计 </el-button>
+          </el-upload>          
+        </el-form-item>
+         <el-form-item>
+          <el-date-picker v-model="state.uploadFilter.EffecDate" type="date" placeholder="选择结算日期"></el-date-picker>          
+        </el-form-item>
+        <el-form-item>            
+            <el-button type="primary" @click="onSettle"> 结算 </el-button>
+          </el-form-item>
+          <!-- <el-form-item>            
+              <el-button type="primary" icon="ele-Download" @click="onDownExcel"> 导出项目统计模板 </el-button>
+            </el-form-item> -->
+      </el-form>
+    </el-card>
+    <el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
+      <el-form :inline="true" @submit.stop.prevent>
+        <el-form-item>
+          <el-select lable="选择项目" v-model="state.filter.projectId" value-key="id" clearable>
+            <el-option v-for="item in state.uploadProjectList" :key="item.id" :label="item.name"
+              :value="item.id"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+            <el-select lable="选择平台" v-model="state.filter.tenantId" value-key="id" clearable>
+              <el-option v-for="item in state.uploadProjectList" :key="item.id" :label="item.name"
+                :value="item.id"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+              <el-select lable="选择结算状态" v-model="state.filter.isSettele"  clearable>
+                <el-option label="已结算" value="1"></el-option>
+                  <el-option label="未结算" value="0"></el-option>
+              </el-select>
+            </el-form-item>
+        <el-form-item>          
+          <el-date-picker v-model="state.filter.effectDate" type="date" placeholder="选择作业时间"></el-date-picker>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <el-card class="my-fill mt8" shadow="never">
+      <el-table v-loading="state.loading" :data="state.listData" row-key="id" height="'100%'"
+        style="width: 100%; height: 100%">
+        <el-table-column label="项目名称" prop="projectName" min-width="120" show-overflow-tooltip />
+        <el-table-column label="所属平台" prop="orgName" min-width="120" show-overflow-tooltip />
+        <el-table-column label="作业日期" min-width="120" show-overflow-tooltip >
+          <template #default="{ row }">
+              {{ row.effectDate.split(" ")[0]}}
+            </template>
+        </el-table-column>
+        <el-table-column label="汇总(有效/总数)" min-width="120" show-overflow-tooltip>
+          <template #default="{ row }">
+            {{ row.vaildCount }}/{{ row.count }}
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" min-width="120" show-overflow-tooltip>
+            <template #default="{ row }">
+              {{ row.isSettle === 1?'已结算':'未结算' }}
+            </template>
+          </el-table-column>
+        <!-- <el-table-column label="操作" width="180" header-align="center" align="center" fixed="right">
+          <template #default="{ row }">
+            <el-button size="small" text type="primary" @click="onDetail(row)">查看明细</el-button>            
+          </template>
+        </el-table-column> -->
+      </el-table>
+    </el-card>    
+  </div>
+</template>
+<script lang="ts" setup name="admin/projectlink">
+import { onMounted, computed, reactive, onBeforeMount, getCurrentInstance } from 'vue'
+import { PageInputProjectStatManagePageInput, ProjectStatManagePageOutput, ProjectSelectOutput } from '/@/api/admin/data-contracts'
+import { ProjectStatApi } from '/@/api/admin/ProjectStat'
+import { ProjectApi } from '/@/api/admin/project'
+import pinia from '/@/stores/index'
+import { useUserInfo } from '/@/stores/userInfo'
+import eventBus from '/@/utils/mitt'
+import { AxiosResponse } from 'axios'
+
+
+const { proxy } = getCurrentInstance() as any
+const storesUserInfo = useUserInfo(pinia)
+
+
+
+const state = reactive({
+  loading: false,
+  tenantFormTitle: '',
+  linkFormTitle: '',
+  uploadFilter: {
+    ProjectId: 0,
+    EffecDate: ''
+  },
+  filter: {
+    projectId: 0,
+    tenantId: 0,
+    isSettele: -1,
+    effectDate: ''
+  },
+  pageInput: {
+    currentPage: 1,
+    pageSize: 20,
+  } as PageInputProjectStatManagePageInput,
+  total: 0,
+  listData: [] as Array<ProjectStatManagePageOutput>,
+  uploadProjectList: [] as Array<ProjectSelectOutput>,
+  queryProjectList: [] as Array<ProjectSelectOutput>,
+  LinkLoading: false,
+  token: storesUserInfo.getToken()
+})
+onMounted(() => {
+  onProjects();
+  onQuery()
+  eventBus.off('refreshProjectStat')
+  eventBus.on('refreshProjectStat', async () => {
+    onQuery()
+  })
+
+})
+onBeforeMount(() => {
+  eventBus.off('refreshProjectStat')
+})
+//查询
+const onQuery = async () => {
+  state.loading = true
+  state.pageInput.filter = state.filter
+  const res = await new ProjectStatApi().getPageManage(state.pageInput).catch(() => {
+    state.loading = false
+  })
+  state.listData = res?.data?.list ?? []
+  state.total = res?.data?.total ?? 0
+  state.loading = false
+}
+//项目列表
+const onProjects = async () => {
+  let input: any = {}
+  const res = await new ProjectApi().getAll(input).catch(() => {
+
+  })
+  state.uploadProjectList = res?.data ?? []
+  state.queryProjectList = res?.data ?? []
+}
+//导入项目统计
+// 上传项目统计请求url
+const StatAction = computed(() => {
+  return import.meta.env.VITE_API_URL + '/api/admin/project-stat/upload'
+})
+// 上传项目统计请求头部
+const StatHeaders = computed(() => {
+  return { Authorization: 'Bearer ' + state.token }
+})
+// 上传项目统计成功
+const onUploadStatSuccess = (res: AxiosResponse) => {
+  state.LinkLoading = false
+  if (!res?.success) {
+    if (res.msg) {
+      proxy.$modal.msgError(res.msg)
+    }
+    return
+  } else {
+    eventBus.off('refreshProjectStat')
+  }
+}
+// 上传项目统计失败
+const onUploadStatError = (error: any) => {
+  state.LinkLoading = false
+  let message = ''
+  if (error.message) {
+    try {
+      message = JSON.parse(error.message)?.msg
+    } catch (err) {
+      message = error.message || ''
+    }
+  }
+  if (message) proxy.$modal.msgError(message)
+}
+//上传项目统计前
+const OnUploadStatBefore = () => {
+  if (state.uploadFilter.ProjectId <= 0) {
+    proxy.$modal.msgError("请选择项目");
+    return false;
+  }  
+  state.token = storesUserInfo.getToken()
+  state.LinkLoading = true
+}
+//结算
+const onSettle = async () => {
+   proxy.$modal
+    .confirm(`请确认是否结算`)
+    .then(async () => {
+      const res = await new ProjectStatApi().Settle(state.uploadFilter, { loading: true }).catch(() => {
+        proxy.$modal.closeLoading()
+      })      
+      if (res?.success) {
+        eventBus.emit('refreshProjectStat')
+      }
+      proxy.$modal.closeLoading()
+    })
+    .catch(() => { })   
+}
+
+</script>