feat: 修改页面,修复bug

main
sankeyangshu 2024-12-02 19:49:22 +08:00
parent b723a0839c
commit 580402c132
37 changed files with 850 additions and 857 deletions

View File

@ -1,30 +1,11 @@
<template>
<el-config-provider :locale="locale" :size="componentSize">
<el-config-provider :locale="zhCn">
<router-view />
</el-config-provider>
</template>
<script lang="ts" setup>
import en from 'element-plus/es/locale/lang/en'; // Element Plus
import zhCn from 'element-plus/es/locale/lang/zh-cn'; // Element Plus
import { computed } from 'vue';
import { useTheme } from '@/hooks/useTheme';
import { useSettingStore } from '@/store/modules/setting';
//
const { initTheme } = useTheme();
initTheme();
//
const settingStore = useSettingStore();
//
const language = computed(() => settingStore.language);
const locale = computed(() => (language.value === 'en' ? en : zhCn));
//
const componentSize = computed(() => settingStore.componentSize);
</script>
<style lang="scss"></style>

View File

@ -1,4 +1,4 @@
import { PageRowsResult } from '@/types';
import { dictionaryListType, dictionaryType, PageRowsResult } from '@/types';
import { BoatInfoPageType, BoatInfoType, vinSearchType } from '@/types/boatInfo';
import http from '@/utils/request';
@ -14,6 +14,7 @@ const api = {
publishHistoryList: '/cargo/sail_schedule/history/publish/page', // 我发布的历史数据
receiveHistoryList: '/cargo/sail_schedule/history/receive/page', // 我接受的历史数据
vinSearch: '/cargo/sail_schedule/vin/query', // 车架号查询
shipList: '/cargo/sail_schedule/ship/list', // 订阅的船期列表
};
/**
@ -112,3 +113,12 @@ export function getReceiveHistoryListAPI(data: Partial<BoatInfoPageType>) {
export function getVinSearchAPI(data: { vin: string }) {
return http.get<vinSearchType[]>(api.vinSearch, data);
}
/**
*
* @param {dictionaryType} data
* @return
*/
export function postSaleShipListAPI(data: dictionaryType) {
return http.get<dictionaryListType[]>(api.shipList, data);
}

View File

@ -6,7 +6,7 @@ import http from '@/utils/request';
const api = {
manifestDel: '/cargo/manifest/delete', // 删除
manifestGet: '/cargo/manifest/get', // 获取
manifestPage: '/cargo/manifest/page', // 分页列表
manifestPage: '/cargo/manifest/subscribe/page', // 我订阅的分页列表
manifestSave: '/cargo/manifest/save', // 保存
manifestImport: '/cargo/manifest/import', // 舱单导入
manifestExport: '/cargo/manifest/tmp/export', // 下载模版

View File

@ -52,5 +52,5 @@ export function noticeSaveAPI(data: Partial<NoticeType>) {
* @return
*/
export function noticeCountAPI() {
return http.get<number>(api.noticeCount);
return http.get<string>(api.noticeCount);
}

View File

@ -6,7 +6,7 @@
:page-size="pageAble.rows"
:page-sizes="[10, 25, 50, 100]"
:total="pageAble.total"
layout="total, sizes, prev, pager, next, jumper"
layout="prev, pager, next, total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></el-pagination>

View File

@ -1,6 +1,5 @@
import NProgress from 'nprogress'; // 进度条
import { router } from '@/router';
import { usePermissionStore } from '@/store/modules/permission';
import { useUserStore } from '@/store/modules/user';
import 'nprogress/nprogress.css'; // 进度条样式
@ -16,7 +15,7 @@ const whiteList = ['/login'];
* @param from
* @param next
*/
router.beforeEach(async (to, from, next) => {
router.beforeEach(async (to, _, next) => {
// 进度条开始
NProgress.start();
@ -36,28 +35,8 @@ router.beforeEach(async (to, from, next) => {
// 如果已登录,重定向到主页
next({ path: '/' });
} else {
// 获取用户权限 store
const permissionStore = usePermissionStore();
// 确定用户是否已通过getInfo获得其权限角色
// const hasRoles = userStore.roles && userStore.roles.length > 0;
try {
// 路由添加进去了没有及时更新 需要重新进去一次拦截
if (!permissionStore.routes.length) {
// 获取权限列表进行接口访问 因为这里页面要切换权限
const accessRoutes = await permissionStore.generateRoutes(userStore.roles);
// 动态添加访问路由表
accessRoutes.forEach((item) => router.addRoute(item));
// 这里相当于push到一个页面 不在进入路由拦截
next({ ...to, replace: true });
} else {
// 如果不传参数就会重新执行路由拦截,重新进到这里
next();
}
} catch (error) {
next(`/login?redirect=${to.path}`);
}
// 如果不传参数就会重新执行路由拦截,重新进到这里
next();
}
} else {
// 用户未登录

View File

@ -1,160 +0,0 @@
import { ElMessage } from 'element-plus';
import { computed } from 'vue';
import { DEFAULT_THEMECOLOR } from '@/config';
import { useSettingStore } from '@/store/modules/setting';
import { headerTheme, menuTheme, sidebarTheme } from '@/styles/theme';
import { getDarkColor, getLightColor } from '@/utils/color';
/**
*
*/
export type ThemeType = 'light' | 'inverted' | 'dark';
/**
*
*/
export type GreyOrWeakType = 'grey' | 'weak';
/**
*
*/
export const useTheme = () => {
// 获取全局状态管理仓库中系统设置状态
const settingStore = useSettingStore();
const themeConfig = computed(() => settingStore.themeConfig);
// 切换暗黑模式
const switchDark = () => {
const body = document.documentElement;
if (themeConfig.value.isDark) {
body.setAttribute('class', 'dark');
} else {
body.setAttribute('class', '');
}
// 同时修改主题色、侧边栏、头部颜色
changeThemeColor(themeConfig.value.themeColor);
changeSideBarTheme();
changeHeaderTheme();
};
/**
*
* @param {string} val
*/
const changeThemeColor = (val: string | null) => {
if (!val) {
val = DEFAULT_THEMECOLOR;
ElMessage({ type: 'success', message: `主题颜色已重置为 ${DEFAULT_THEMECOLOR}` });
}
// 计算主题颜色变化
document.documentElement.style.setProperty('--el-color-primary', val);
document.documentElement.style.setProperty(
'--el-color-primary-dark-2',
themeConfig.value.isDark ? `${getLightColor(val, 0.2)}` : `${getDarkColor(val, 0.3)}`
);
for (let i = 1; i <= 9; i++) {
const primaryColor = themeConfig.value.isDark
? `${getDarkColor(val, i / 10)}`
: `${getLightColor(val, i / 10)}`;
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, primaryColor);
}
settingStore.setThemeConfig('themeColor', val);
};
/**
*
* @param {GreyOrWeakType} type
* @param {boolean} value
*/
const changeGreyOrWeak = (type: GreyOrWeakType, value: boolean) => {
const body = document.body as HTMLElement;
if (!value) return body.removeAttribute('style');
const styles: Record<GreyOrWeakType, string> = {
grey: 'filter: grayscale(1)',
weak: 'filter: invert(80%)',
};
body.setAttribute('style', styles[type]);
const propName = type === 'grey' ? 'isWeak' : 'isGrey';
settingStore.setThemeConfig(propName, false);
};
/**
*
*/
const changeMenuTheme = () => {
let type: ThemeType = 'light';
// 判断是否是暗黑主题
if (themeConfig.value.isDark) {
type = 'dark';
}
const theme = menuTheme[type];
// 遍历写入样式
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
};
/**
*
*/
const changeSideBarTheme = () => {
let type: ThemeType = 'light';
// 判断是否是暗黑主题
if (themeConfig.value.isDark) {
type = 'dark';
}
const theme = sidebarTheme[type];
// 遍历写入样式
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
changeMenuTheme();
};
/**
*
*/
const changeHeaderTheme = () => {
let type: ThemeType = 'light';
// 判断是否是暗黑主题
if (themeConfig.value.isDark) {
type = 'dark';
}
const theme = headerTheme[type];
// 遍历写入样式
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
changeMenuTheme();
};
// 初始化主题
const initTheme = () => {
switchDark();
// 判断是否是灰色模式
if (themeConfig.value.isGrey) changeGreyOrWeak('grey', true);
// 判断是否色弱模式
if (themeConfig.value.isWeak) changeGreyOrWeak('weak', true);
};
return {
switchDark,
changeThemeColor,
changeGreyOrWeak,
initTheme,
changeSideBarTheme,
changeHeaderTheme,
};
};

View File

@ -15,7 +15,17 @@
@click="onClickMenu(item)"
>
<svg-icon :icon="item.icon" className="icon"></svg-icon>
<span>{{ item.title }}</span>
<template v-if="item.path === '/notice'">
<el-badge
:value="noticeCount"
:show-zero="false"
:offset="[10, 0]"
badge-style="background-color: #ff0;color: #000;"
>
<span>{{ item.title }}</span>
</el-badge>
</template>
<span v-else>{{ item.title }}</span>
</div>
</div>
</template>
@ -23,6 +33,7 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useSettingStore } from '@/store/modules/setting';
interface menuListType {
path: string;
@ -30,6 +41,11 @@ interface menuListType {
icon: string;
}
//
const settingStore = useSettingStore();
const noticeCount = computed(() => settingStore.noticeCount);
// const noticeCount = ref(count.value);
//
const menuList = ref<menuListType[]>([
{

View File

@ -99,7 +99,6 @@ onMounted(async () => {
height: 100%;
overflow: hidden;
background-color: #7f7f7f;
box-shadow: 2px 0 6px rgb(0 21 41 / 35%);
.side-logo {
width: 100%;
height: 170px;

View File

@ -5,7 +5,7 @@
</el-aside>
<el-container>
<el-header>
<el-header class="header">
<Header />
</el-header>
@ -33,7 +33,7 @@ import Sidebar from './Sidebar/index.vue';
background-color: #555c64;
border-right: 1px solid var(--el-aside-border-color);
}
.el-header {
.header {
box-sizing: border-box;
display: flex;
align-items: center;
@ -42,6 +42,7 @@ import Sidebar from './Sidebar/index.vue';
padding: 0 20px;
color: #fff;
background-color: #9c000c;
box-shadow: 0 5px 5px rgb(0 0 0 / 34.9%);
}
.el-main {
background-color: #555;

View File

@ -1,48 +0,0 @@
import { defineStore } from 'pinia';
import { RouteRecordRaw } from 'vue-router';
import { asyncRoutes, constantRoutes, notFoundRouter } from '@/router';
import { roleResultType } from '@/types/role';
import { filterAsyncRoutes, filterKeepAlive } from '@/utils/routers';
interface permissionStateType {
routes: RouteRecordRaw[];
dynamicRoutes: RouteRecordRaw[];
}
export const usePermissionStore = defineStore({
// id: 必须的,在所有 Store 中唯一
id: 'permissionState',
// state: 返回对象的函数
state: (): permissionStateType => ({
routes: [], // 路由
dynamicRoutes: [], // 动态路由
}),
getters: {
permission_routes: (state) => {
return state.routes;
},
keepAliveRoutes: () => {
return filterKeepAlive(asyncRoutes);
},
},
// 可以同步 也可以异步
actions: {
// 生成路由
generateRoutes(roles: roleResultType[]): Promise<RouteRecordRaw[]> {
return new Promise((resolve) => {
// 在这判断是否有权限,哪些角色拥有哪些权限
let accessedRoutes: RouteRecordRaw[] = [];
if (roles && roles.length) {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
}
accessedRoutes = accessedRoutes.concat(notFoundRouter);
this.routes = constantRoutes.concat(accessedRoutes);
this.dynamicRoutes = accessedRoutes;
resolve(accessedRoutes);
});
},
},
});

View File

@ -1,101 +1,23 @@
import { defineStore } from 'pinia';
import { DEFAULT_THEMECOLOR } from '@/config';
/**
*
*/
export type componentSizeType = 'large' | 'default' | 'small';
/**
*
*/
export type layoutType = 'vertical' | 'classic' | 'transverse' | 'columns';
interface settingsStateType {
isCollapse: boolean;
language: string;
isReload: boolean;
componentSize: componentSizeType;
themeConfig: themeConfigType;
noticeCount: number;
}
/**
*
*/
interface themeConfigType {
layout: layoutType;
showSetting: boolean;
themeColor: string;
isDark: boolean;
isGrey: boolean;
isWeak: boolean;
showTag: boolean;
fixedHeader: boolean;
showLogo: boolean;
uniqueOpened: boolean;
}
/**
* key
*/
export type themeConfigKeyType = keyof themeConfigType;
export const useSettingStore = defineStore({
// id: 必须的,在所有 Store 中唯一
id: 'settingState',
// state: 返回对象的函数
state: (): settingsStateType => ({
isCollapse: false, // 是否收缩左侧菜单栏
language: 'zhCn', // 国际化-默认是zhCn
isReload: true, // 是否刷新当前页
componentSize: 'default', // 组件大小切换
themeConfig: {
layout: 'classic', // 布局模式 (纵向vertical | 经典classic | 横向transverse | 分栏columns)
showSetting: false, // 显示设置
themeColor: DEFAULT_THEMECOLOR, // 主题颜色
isDark: false, // 深色模式 切换暗黑模式
isGrey: false, // 灰色模式
isWeak: false, // 色弱模式
showTag: true, // tagsView 是否展示 默认展示
fixedHeader: true, // 固定header
showLogo: true, // 显示侧边栏Logo
uniqueOpened: true, // 是否只保持一个子菜单的展开
}, // 主题设置
noticeCount: 0,
}),
// actions: 可以同步 也可以异步
actions: {
// 设置左侧菜单的展开与收缩
setCollapse(value: boolean) {
this.isCollapse = value;
// 设置未读消息数量
setNoticeCount(value: number) {
this.noticeCount = value;
},
// 设置国际化
setLanguage(value: string) {
this.language = value;
},
// 刷新当前页
setReload() {
this.isReload = false;
setTimeout(() => {
this.isReload = true;
}, 50);
},
// 设置组件尺寸
setComponentSize(value: componentSizeType) {
this.componentSize = value;
},
// 设置主题
setThemeConfig(key: themeConfigKeyType, val: string | boolean) {
(this.themeConfig[key] as string | boolean) = val;
},
},
// 进行持久化存储
persist: {
// 本地存储的名称
key: 'settingState',
// 保存的位置
storage: window.localStorage, // localstorage
},
});

View File

@ -1,125 +0,0 @@
import { defineStore } from 'pinia';
import { RouteRecordRaw } from 'vue-router';
import { router } from '@/router';
interface tagsViewStateType {
activeTabsValue: string;
visitedViews: RouteRecordRaw[];
cachedViews: any[];
}
export const useTagsViewStore = defineStore({
// id: 必须的,在所有 Store 中唯一
id: 'tagsViewState',
// state: 返回对象的函数
state: (): tagsViewStateType => ({
activeTabsValue: '/home', // 选中的tagsView
visitedViews: [], // 选中过的路由表
cachedViews: [], // 使用 keepAlive 时的缓存
}),
getters: {},
// 可以同步 也可以异步
actions: {
// 选中tagsView
setTabsMenuValue(val: string) {
this.activeTabsValue = val;
},
// 新增tagsView
addView(view: RouteRecordRaw) {
this.addVisitedView(view);
},
addVisitedView(view: RouteRecordRaw) {
this.setTabsMenuValue(view.path);
if (this.visitedViews.some((v) => v.path === view.path) || !view.meta) return;
this.visitedViews.push(
Object.assign({}, view, {
title: view.meta.title || 'no-name',
})
);
if (view.meta.keepAlive && view.name) {
this.cachedViews.push(view.name);
}
},
// 移除tagsView
removeView(routes: RouteRecordRaw[]) {
return new Promise((resolve) => {
this.visitedViews = this.visitedViews.filter(
(item) => !routes.includes((item as any).path)
);
resolve(null);
});
},
delView(activeTabPath: any) {
return new Promise((resolve) => {
this.delVisitedView(activeTabPath);
this.delCachedView(activeTabPath);
resolve({
visitedViews: [...this.visitedViews],
cachedViews: [...this.cachedViews],
});
});
},
delVisitedView(path: string) {
return new Promise((resolve) => {
this.visitedViews = this.visitedViews.filter((v) => {
if (!v.meta) return;
return v.path !== path || v.meta.affix;
});
this.cachedViews = this.cachedViews.filter((v) => {
return v.path !== path || v.meta.affix;
});
resolve([...this.visitedViews]);
});
},
delCachedView(view: RouteRecordRaw) {
return new Promise((resolve) => {
const index = this.cachedViews.indexOf(view.name);
index > -1 && this.cachedViews.splice(index, 1);
resolve([...this.cachedViews]);
});
},
// 删除以后切换到下一个
toLastView(activeTabPath: string) {
const index = this.visitedViews.findIndex((item) => item.path === activeTabPath);
const nextTab = this.visitedViews[index + 1] || this.visitedViews[index - 1];
if (!nextTab) return;
router.push(nextTab.path);
this.addVisitedView(nextTab);
},
clearVisitedView() {
this.delAllViews();
},
// 删除全部标签
delAllViews() {
this.visitedViews = this.visitedViews.filter((v) => v.meta && v.meta.affix);
this.cachedViews = this.visitedViews.filter((v) => v.meta && v.meta.affix);
},
// 删除其他标签
delOtherViews(path: string) {
this.visitedViews = this.visitedViews.filter((item) => {
return item.path === path || (item.meta && item.meta.affix);
});
this.cachedViews = this.visitedViews.filter((item) => {
return item.path === path || (item.meta && item.meta.affix);
});
},
// 回到首页
goHome() {
this.activeTabsValue = '/home';
router.push({ path: '/home' });
},
updateVisitedView(view: RouteRecordRaw) {
for (let v of this.visitedViews) {
if (v.path === view.path) {
v = Object.assign(v, view);
break;
}
}
},
},
});

View File

@ -5,7 +5,7 @@ import { loginDataType, userInfoType } from '@/types/user';
interface userStateType {
token: string;
userInfo: userInfoType | {};
userInfo: Partial<userInfoType>;
routeName: string;
}

View File

@ -242,7 +242,7 @@ export interface ShipChartPageType extends PageRowsType {
*
*/
export interface BoatInfoPageType extends PageRowsType {
enterpriseId: number;
enterpriseId: number | string;
shipId: number;
voyage: string;
shipRouteId: number;

View File

@ -46,6 +46,7 @@ export interface ManifestPageType extends PageRowsType {
pubEnterpriseId: number;
pubWharfId: number;
subSchedule: string;
deck: number;
subLoadWharfIds: any[];
subWharfId: number;
}

View File

@ -12,41 +12,14 @@ export const checkStatus = (status: number, message?: string | Array<string>): v
let errMsg = ''; // 错误提示信息
switch (status) {
case 400:
ElMessage.error('请求失败!请您稍后重试');
break;
case 401:
ElMessage.error('登录失效!请您重新登录');
// 退出登录
userStore.logout(true);
break;
case 403:
if (message) {
errMsg = typeof message === 'string' ? message : message[0];
}
ElMessage.error(errMsg || '当前账号无权限访问!');
break;
case 404:
ElMessage.error('你所访问的资源不存在!');
break;
case 405:
ElMessage.error('请求方式错误!请您稍后重试');
break;
case 408:
ElMessage.error('请求超时!请您稍后重试');
break;
case 500:
ElMessage.error('服务异常!');
break;
case 502:
ElMessage.error('网络错误!');
break;
case 503:
ElMessage.error('服务不可用,服务器暂时过载或维护!');
break;
case 504:
ElMessage.error('网络超时!');
break;
default:
if (message) {
errMsg = typeof message === 'string' ? message : message[0];

View File

@ -76,7 +76,7 @@ service.interceptors.response.use(
}
// 根据响应的错误状态码,做不同的处理
if (response) {
checkStatus(response.status);
checkStatus(response.status, (response.data as any).message);
}
return Promise.reject(error);
}

View File

@ -18,10 +18,16 @@
</div>
<!-- 表格 -->
<div class="footer-table">
<el-table v-loading="tableLoading" :data="tableState.tableData" max-height="180">
<el-table-column prop="name" label="接口名称" align="center" width="180" />
<el-table-column prop="remark" label="功能描述" align="center" />
<el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
<el-table
v-loading="loadMoreState === 'loading'"
:data="tableData"
max-height="220px"
v-el-table-infinite-scroll="tableDataLoad"
:infinite-scroll-disabled="disabled"
>
<el-table-column prop="name" label="接口名称" align="left" width="480px" />
<el-table-column prop="remark" label="功能描述" align="left" />
<el-table-column prop="operator" label="操作" width="150px" align="left" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" link @click="onClickOpenDialog(scope.row)">
点击查看
@ -39,10 +45,9 @@
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { computed, ref } from 'vue';
import { useRouter } from 'vue-router';
import { postApiPageAPI } from '@/api/ApiManage';
import { useTable } from '@/hooks/useTable';
import { ApiManageType } from '@/types/help';
import ApiDialog from './ApiDialog.vue';
@ -62,27 +67,45 @@ const onClickChangeApiType = async (item: apiInterface) => {
if (item === currentApiType.value) return;
currentApiType.value = item;
//
tableState.value.searchParam = {
apiType: item,
};
await searchTable();
currentPage.value = 1;
await getTableDataList();
};
//
const tableData = ref<ApiManageType[]>([]);
// -
const currentPage = ref(1);
//
const loadMoreState = ref('loading');
const disabled = computed(() => loadMoreState.value === 'finished');
//
const { getTableList, tableState, searchTable } = useTable({
api: postApiPageAPI,
});
const getTableDataList = async () => {
loadMoreState.value = 'loading';
onMounted(async () => {
tableState.value.searchInitParam = {
const { data } = await postApiPageAPI({
page: currentPage.value,
rows: 10,
apiType: currentApiType.value,
};
});
await getTableList();
});
tableData.value = [...tableData.value, ...data.records];
const tableLoading = ref(false);
currentPage.value++;
//
if (currentPage.value > Number(data.pages) || data.records.length < 10) {
loadMoreState.value = 'finished';
} else {
loadMoreState.value = 'load';
}
};
const tableDataLoad = async () => {
await getTableDataList();
};
// /
const apiDialogRef = ref<InstanceType<typeof ApiDialog> | null>(null);
@ -130,7 +153,7 @@ const onClickOpenDialog = (row: ApiManageType) => {
color: #b4b4b4;
}
.title {
font-size: 28px;
font-size: 32px;
font-weight: 700;
}
.api-btn {
@ -162,10 +185,10 @@ const onClickOpenDialog = (row: ApiManageType) => {
}
.footer-pagination {
box-sizing: border-box;
flex: 1;
width: 100%;
height: 520px;
padding: 20px;
margin-top: 20px;
margin-top: 30px;
background-color: #fff;
}
}

View File

@ -4,16 +4,23 @@
<div class="footer">
<!-- 表格 -->
<div class="footer-table">
<el-table v-loading="tableLoading" :data="tableState.tableData" max-height="100%">
<el-table-column prop="enterprise.name" label="企业" align="center" width="150" />
<el-table-column prop="ship.name" label="船舶" align="center" width="150" />
<el-table
v-loading="loadMoreState === 'loading'"
:data="tableData"
max-height="400px"
v-el-table-infinite-scroll="tableDataLoad"
:infinite-scroll-disabled="disabled"
>
<!-- <el-table-column prop="enterprise.name" label="企业" align="center" width="150" /> -->
<el-table-column prop="ship.name" label="船名" align="center" width="150" />
<el-table-column prop="voyage" label="航次" align="center" width="180" />
<el-table-column prop="shipRoute.name" label="航线" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="loadWharf.name" label="装货码头" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="dischargeWharf.name" label="卸货码头" align="center" width="150" />
<el-table-column prop="carNumPlan" label="计划商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column prop="carNumActual" label="实际商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column
prop="spareNumActual"
label="实际件杂货数量"
@ -32,20 +39,11 @@
align="center"
width="200"
/>
<el-table-column prop="shipStatus" label="船舶状态" align="center" width="150" />
<el-table-column prop="tradeType" label="贸易类型" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="createDate" label="创建时间" align="center" width="180" />
<el-table-column prop="shipStatus" label="当前状态" align="center" width="150" />
<el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
<template #default="scope">
<el-button
type="primary"
size="small"
icon="View"
link
@click="onClickOpenDetail(scope.row)"
>
<el-button type="primary" size="small" link @click="onClickOpenDetail(scope.row)">
查看
</el-button>
</template>
@ -58,6 +56,7 @@
<svg-icon icon="Export" className="icon"></svg-icon>
船期导出
</div>
<div class="notice">仅列出最近十日收到的订阅船期信息</div>
</div>
</div>
</div>
@ -66,44 +65,61 @@
<script lang="ts" setup>
import dayjs from 'dayjs';
import { ElMessage, ElMessageBox } from 'element-plus';
import { onMounted, reactive, ref } from 'vue';
import { computed, ref } from 'vue';
import {
getSailScheduleExportAPI,
getSailScheduleGetAPI,
getSailSchedulePageAPI,
} from '@/api/Boat/info';
import { useTable } from '@/hooks/useTable';
import { PageRowsResult } from '@/types';
import { BoatInfoType } from '@/types/boatInfo';
const handleTableData = (data: PageRowsResult<BoatInfoType>) => {
const { records } = data;
for (const item of records) {
item.departureDatePlan = dayjs(item.departureDatePlan).format('YYYY-MM-DD HH:mm:ss');
item.departureDateActual = dayjs(item.departureDateActual).format('YYYY-MM-DD HH:mm:ss');
const handleTableData = (data: BoatInfoType[]) => {
for (const item of data) {
item.departureDatePlan = dayjs(item.departureDatePlan).format('YYYY-MM-DD HH:mm');
item.departureDateActual = dayjs(item.departureDateActual).format('YYYY-MM-DD HH:mm');
}
return data;
};
//
const { getTableList, tableState } = useTable({
api: getSailSchedulePageAPI,
dataCallBack: handleTableData,
});
//
const tableData = ref<BoatInfoType[]>([]);
onMounted(async () => {
await getTableList();
});
// -
const currentPage = ref(1);
//
const searchTableForm = reactive({
shipId: '',
voyage: '',
});
//
const loadMoreState = ref('loading');
const disabled = computed(() => loadMoreState.value === 'finished');
//
const tableLoading = ref(false);
//
const getTableDataList = async () => {
loadMoreState.value = 'loading';
const { data } = await getSailSchedulePageAPI({
page: currentPage.value,
rows: 10,
});
//
const dataRecords = handleTableData(data.records);
tableData.value = [...tableData.value, ...dataRecords];
currentPage.value++;
//
if (currentPage.value > Number(data.pages) || data.records.length < 10) {
loadMoreState.value = 'finished';
} else {
loadMoreState.value = 'load';
}
};
const tableDataLoad = async () => {
await getTableDataList();
};
//
const onClickExport = () => {
ElMessageBox.confirm(`你确定要导出船期信息吗?`, '温馨提示', {
confirmButtonText: '确定',
@ -114,15 +130,9 @@ const onClickExport = () => {
.then(async () => {
try {
let params: any = {
rows: tableState.value.pageAble.rows,
page: tableState.value.pageAble.page,
rows: 10,
page: currentPage.value,
};
if (searchTableForm.shipId) {
params.shipId = searchTableForm.shipId;
}
if (searchTableForm.voyage) {
params.voyage = searchTableForm.voyage;
}
const { data } = await getSailScheduleExportAPI(params);
const blob = new Blob([data], {
type: 'application/vnd.ms-excel;charset=utf-8',
@ -189,7 +199,8 @@ const onClickOpenDetail = async (row: BoatInfoType) => {
.footer-pagination {
box-sizing: border-box;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
width: 100%;
margin-top: 40px;
.btn-export {
@ -210,6 +221,21 @@ const onClickOpenDetail = async (row: BoatInfoType) => {
color: #80fffe;
}
}
.notice {
display: flex;
align-items: center;
justify-content: center;
width: 700px;
height: 40px;
margin-top: 30px;
overflow: hidden;
font-size: 16px;
color: #d7d7d7;
text-overflow: ellipsis;
white-space: nowrap;
background-color: rgb(127 127 127 / 100%);
border-radius: 20px;
}
}
}
}

View File

@ -5,7 +5,7 @@
<div class="content" v-html="agreeText?.content"></div>
</div>
<div class="agree-btn" v-if="textType === 'AGREEMENT' && !isAgree">
<el-checkbox v-model="checked" size="large">
<el-checkbox v-model="checked">
<div class="btn-text">我已经完整阅读并同意用户协议</div>
</el-checkbox>
@ -95,7 +95,20 @@ onMounted(async () => {
align-items: center;
width: 100%;
height: 80px;
:deep(.el-checkbox) {
--el-checkbox-checked-bg-color: #facd91;
}
:deep(.el-checkbox .el-checkbox__inner) {
width: 32px;
height: 32px;
}
:deep(.el-checkbox__inner::after) {
top: 6px;
left: 14px;
height: 18px;
}
.btn-text {
font-size: 18px;
color: #fff;
}
.btn {

View File

@ -82,7 +82,7 @@ const onClickOpenDialog = async (row: HelpType) => {
height: 100%;
.list-wrap {
box-sizing: border-box;
width: 30%;
width: 300px;
height: 500px;
overflow: auto;
overflow: hidden;

View File

@ -2,16 +2,18 @@
<div class="table">
<div class="header">
<svg-icon icon="return" className="side-icon" @click="onClickReturn"></svg-icon>
<div class="title">帮助信息</div>
<div class="api-btn">
<div
class="btn"
:class="{ 'btn-active': currentNav === item }"
v-for="item in helpDocsList"
:key="item"
@click="onClickChangeNav(item)"
>
{{ item }}
<div class="header-right">
<div class="title">帮助信息</div>
<div class="api-btn">
<div
class="btn"
:class="{ 'btn-active': currentNav === item }"
v-for="item in helpDocsList"
:key="item"
@click="onClickChangeNav(item)"
>
{{ item }}
</div>
</div>
</div>
</div>
@ -49,11 +51,13 @@ const onClickChangeNav = (item: string) => {
<style lang="scss" scoped>
.table {
position: relative;
box-sizing: border-box;
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
height: 100%;
padding-right: 40px;
.header {
box-sizing: border-box;
display: flex;
@ -70,31 +74,37 @@ const onClickChangeNav = (item: string) => {
line-height: normal;
color: #b4b4b4;
}
.title {
font-size: 28px;
font-weight: 700;
}
.api-btn {
.header-right {
display: flex;
align-items: center;
justify-content: flex-end;
.btn {
.title {
box-sizing: border-box;
padding-right: 120px;
font-size: 32px;
font-weight: 700;
}
.api-btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 50px;
margin-left: 10px;
font-size: 18px;
color: #d7d7d7;
background-color: rgb(51 51 51 / 100%);
border-radius: 75px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
}
.btn-active {
color: #000;
background-color: #f2f2f2;
box-shadow: none;
justify-content: flex-end;
.btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 50px;
margin-left: 10px;
font-size: 18px;
color: #d7d7d7;
background-color: rgb(51 51 51 / 100%);
border-radius: 75px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
}
.btn-active {
color: #000;
background-color: #f2f2f2;
box-shadow: none;
}
}
}
}

View File

@ -11,21 +11,22 @@
<!-- 表格 -->
<div class="footer-table">
<el-table
v-loading="tableLoading"
v-loading="loadMoreState === 'loading'"
:data="tableData"
max-height="400px"
v-el-table-infinite-scroll="tableDataLoad"
:infinite-scroll-disabled="disabled"
>
<el-table-column prop="enterprise.name" label="企业" align="center" width="150" />
<el-table-column prop="ship.name" label="船" align="center" width="150" />
<!-- <el-table-column prop="enterprise.name" label="企业" align="center" width="150" /> -->
<el-table-column prop="ship.name" label="船" align="center" width="150" />
<el-table-column prop="voyage" label="航次" align="center" width="180" />
<el-table-column prop="shipRoute.name" label="航线" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="loadWharf.name" label="装货码头" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="dischargeWharf.name" label="卸货码头" align="center" width="150" />
<el-table-column prop="carNumPlan" label="计划商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column prop="carNumActual" label="实际商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column
prop="spareNumActual"
label="实际件杂货数量"
@ -44,11 +45,8 @@
align="center"
width="200"
/>
<el-table-column prop="shipStatus" label="船舶状态" align="center" width="150" />
<el-table-column prop="tradeType" label="贸易类型" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="createDate" label="创建时间" align="center" width="180" />
<el-table-column prop="shipStatus" label="当前状态" align="center" width="150" />
</el-table>
</div>
<!-- 分页 -->
@ -60,13 +58,18 @@
</template>
<script lang="ts" setup>
import dayjs from 'dayjs';
import { computed, ref } from 'vue';
import { getPublishHistoryListAPI } from '@/api/Boat/info';
import { BoatInfoType } from '@/types/boatInfo';
// TODO:
//
const tableLoading = ref(false);
const handleTableData = (data: BoatInfoType[]) => {
for (const item of data) {
item.departureDatePlan = dayjs(item.departureDatePlan).format('YYYY-MM-DD HH:mm');
item.departureDateActual = dayjs(item.departureDateActual).format('YYYY-MM-DD HH:mm');
}
return data;
};
//
const tableData = ref<BoatInfoType[]>([]);
@ -85,16 +88,17 @@ const getTableDataList = async () => {
rows: 10,
});
tableLoading.value = false;
//
const dataRecords = handleTableData(data.records);
tableData.value = [...tableData.value, ...dataRecords];
tableData.value = [...tableData.value, ...data.records];
currentPage.value++;
//
if (currentPage.value > Number(data.pages) || data.records.length < 10) {
loadMoreState.value = 'finished';
} else {
tableLoading.value = true;
loadMoreState.value = 'loading';
loadMoreState.value = 'load';
}
};

View File

@ -10,16 +10,23 @@
<div class="footer">
<!-- 表格 -->
<div class="footer-table">
<el-table v-loading="tableLoading" :data="tableState.tableData" max-height="100%">
<el-table-column prop="enterprise.name" label="企业" align="center" width="150" />
<el-table-column prop="ship.name" label="船舶" align="center" width="150" />
<el-table
v-loading="loadMoreState === 'loading'"
:data="tableData"
max-height="400px"
v-el-table-infinite-scroll="tableDataLoad"
:infinite-scroll-disabled="disabled"
>
<!-- <el-table-column prop="enterprise.name" label="企业" align="center" width="150" /> -->
<el-table-column prop="ship.name" label="船名" align="center" width="150" />
<el-table-column prop="voyage" label="航次" align="center" width="180" />
<el-table-column prop="shipRoute.name" label="航线" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="loadWharf.name" label="装货码头" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="dischargeWharf.name" label="卸货码头" align="center" width="150" />
<el-table-column prop="carNumPlan" label="计划商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column prop="carNumActual" label="实际商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column
prop="spareNumActual"
label="实际件杂货数量"
@ -38,11 +45,8 @@
align="center"
width="200"
/>
<el-table-column prop="shipStatus" label="船舶状态" align="center" width="150" />
<el-table-column prop="tradeType" label="贸易类型" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="createDate" label="创建时间" align="center" width="180" />
<el-table-column prop="shipStatus" label="当前状态" align="center" width="150" />
</el-table>
</div>
<!-- 分页 -->
@ -54,21 +58,53 @@
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import dayjs from 'dayjs';
import { computed, ref } from 'vue';
import { getReceiveHistoryListAPI } from '@/api/Boat/info';
import { useTable } from '@/hooks/useTable';
import { BoatInfoType } from '@/types/boatInfo';
//
const { getTableList, tableState } = useTable({
api: getReceiveHistoryListAPI,
});
const handleTableData = (data: BoatInfoType[]) => {
for (const item of data) {
item.departureDatePlan = dayjs(item.departureDatePlan).format('YYYY-MM-DD HH:mm');
item.departureDateActual = dayjs(item.departureDateActual).format('YYYY-MM-DD HH:mm');
}
return data;
};
onMounted(async () => {
await getTableList();
});
//
const tableData = ref<BoatInfoType[]>([]);
//
const tableLoading = ref(false);
// -
const currentPage = ref(1);
//
const loadMoreState = ref('loading');
const disabled = computed(() => loadMoreState.value === 'finished');
//
const getTableDataList = async () => {
const { data } = await getReceiveHistoryListAPI({
page: currentPage.value,
rows: 10,
});
//
const dataRecords = handleTableData(data.records);
tableData.value = [...tableData.value, ...dataRecords];
currentPage.value++;
//
if (currentPage.value > Number(data.pages) || data.records.length < 10) {
loadMoreState.value = 'finished';
} else {
loadMoreState.value = 'load';
}
};
const tableDataLoad = async () => {
await getTableDataList();
};
</script>
<style lang="scss" scoped>

View File

@ -70,7 +70,8 @@ import { ElMessage, FormInstance } from 'element-plus';
import { onMounted, reactive, ref } from 'vue';
import { getStatisticsAPI } from '@/api/ApiManage';
import { getVinSearchAPI } from '@/api/Boat/info';
import { noticePageAPI } from '@/api/Notice';
import { noticeCountAPI, noticePageAPI } from '@/api/Notice';
import { useSettingStore } from '@/store/modules/setting';
import { vinSearchType } from '@/types/boatInfo';
import { OutImportVoyages } from '@/types/help';
import { NoticeType } from '@/types/notice';
@ -106,8 +107,15 @@ const onClickSearch = async (formEl: FormInstance | undefined) => {
const noticeData = ref<NoticeType>();
// store
const settingStore = useSettingStore();
//
const getNoticeList = async () => {
//
const { data: count } = await noticeCountAPI();
settingStore.setNoticeCount(Number(count));
const { data } = await noticePageAPI({
page: 1,
rows: 10,
@ -142,6 +150,9 @@ const outImportVoyages = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -182,6 +193,9 @@ const outExportVoyages = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -222,6 +236,9 @@ const inTotalVoyages = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -262,6 +279,9 @@ const inYearVoyages = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -302,6 +322,9 @@ const outImportCargos = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -342,6 +365,9 @@ const outExportCargos = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -382,6 +408,9 @@ const inTotalCargos = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -422,6 +451,9 @@ const inYearCargos = ref({
orient: 'vertical',
left: 'left',
top: 'middle',
textStyle: {
color: '#fff',
},
},
series: [
{
@ -549,6 +581,7 @@ onMounted(async () => {
.charts {
display: flex;
flex-direction: column;
margin-right: 50px;
}
}
.home-footer {

View File

@ -420,7 +420,7 @@ const onClickSubmitRegister = (formEl: FormInstance | undefined) => {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-around;
justify-content: center;
width: 96%;
height: 94%;
.login-welcome {
@ -463,8 +463,9 @@ const onClickSubmitRegister = (formEl: FormInstance | undefined) => {
}
.login-form {
box-sizing: border-box;
width: 400px;
padding: 60px 45px;
width: 418px;
padding: 60px 59px;
margin-left: 280px;
background-color: #fff;
border-radius: 7px;
box-shadow: 0 0 13px rgb(0 0 0 / 34.9%);

View File

@ -1,70 +1,113 @@
<template>
<div class="table">
<div class="manifest">
<div class="header">
<el-form :inline="true" :model="searchTableForm" ref="tableFormRef">
<el-form-item>
<RemoteSelect
v-model:value="searchTableForm.shipId"
placeholder="请选择船名"
:api="postShipListAPI"
style="width: 120px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="onClickSearch"></el-button>
<el-button icon="Refresh" @click="onClickResetForm(tableFormRef)"></el-button>
</el-form-item>
</el-form>
<div>
<el-button type="success" @click="onClickExport"></el-button>
<div class="header-left">
<svg-icon icon="return" className="side-icon" @click="onClickReturn"></svg-icon>
<div class="search">
<el-form :model="searchTableForm" ref="tableFormRef">
<el-form-item>
<div class="search-top">
<div class="card-title">请选择船名</div>
<div class="card-text">
<RemoteSelect
v-model:value="searchTableForm.scheduleId"
:api="postSaleShipListAPI"
placeholder=""
/>
</div>
</div>
</el-form-item>
<div class="search-bottom">
<div class="bottom-one" @click="onClickSearch"></div>
<div class="bottom-two" @click="onClickResetForm(tableFormRef)"></div>
</div>
</el-form>
</div>
<div class="deck">
<div
class="deck-btn"
v-for="item in 12"
:class="{ 'deck-btn-active': item === Number(searchTableForm.deck) }"
:key="item"
@click="onClickOpenDeck(item)"
>
{{ item < 10 ? '0' + item : item }}
</div>
</div>
</div>
<div class="title">舱单信息</div>
<div class="api-btn">
<div
class="btn"
:class="{ 'btn-active': '舱单信息' === item }"
v-for="item in subscribeNavList"
:key="item"
@click="onClickChangeSubscribeType(item)"
>
{{ item }}
</div>
</div>
</div>
<div class="footer">
<!-- 表格 -->
<div class="footer-table">
<el-table
v-loading="tableLoading"
:data="tableState.tableData"
style="width: 100%; height: 100%"
>
<el-table-column prop="schedule.name" label="船期" align="center" width="120" />
<el-table-column prop="deck" label="舱层" align="center" width="80" />
<el-table-column prop="cabin" label="舱段" align="center" width="80" />
<el-table-column prop="billNo" label="提单号" align="center" width="160" />
<el-table-column prop="goodsName" label="货名" align="center" width="120" />
<el-table-column prop="brand.name" label="品牌" align="center" width="120" />
<el-table-column prop="model" label="型号" align="center" width="120" />
<el-table-column prop="shippingMark" label="唛头" align="center" width="180" />
<el-table-column prop="carNum" label="车数量" align="center" width="100" />
<el-table-column prop="spareNum" label="件杂货数" align="center" width="100" />
<el-table-column prop="weight" label="重量" align="center" width="100" />
<el-table-column prop="volume" label="体积" align="center" width="100" />
<el-table-column prop="consigner" label="发货人" align="center" width="180" />
<el-table-column prop="consignee" label="收货人" align="center" width="180" />
<el-table-column prop="goodsStatus" label="货物状态" align="center" width="150" />
<el-table-column prop="createDate" label="创建时间" align="center" width="180" />
<el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
<template #default="scope">
<el-button
type="primary"
size="small"
icon="View"
link
@click="onClickOpenDetail(scope.row)"
>
查看明细
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="footer-pagination">
<Pagination
:pageAble="tableState.pageAble"
:handle-size-change="handleSizeChange"
:handle-current-change="handleCurrentChange"
/>
<div class="content">
<div class="footer">
<div class="footer-table">
<el-table
v-loading="tableLoading"
:data="tableState.tableData"
style="width: 100%; height: 100%"
>
<el-table-column prop="schedule.name" label="船期" align="center" width="120" />
<el-table-column prop="deck" label="舱层" align="center" width="80" />
<el-table-column prop="cabin" label="舱段" align="center" width="80" />
<el-table-column prop="billNo" label="提单号" align="center" width="160" />
<el-table-column prop="goodsName" label="货名" align="center" width="120" />
<el-table-column prop="brand.name" label="品牌" align="center" width="120" />
<el-table-column prop="model" label="型号" align="center" width="120" />
<el-table-column prop="shippingMark" label="唛头" align="center" width="180" />
<el-table-column prop="carNum" label="车数量" align="center" width="100" />
<el-table-column prop="spareNum" label="件杂货数" align="center" width="100" />
<el-table-column prop="weight" label="重量" align="center" width="100" />
<el-table-column prop="volume" label="体积" align="center" width="100" />
<el-table-column prop="consigner" label="发货人" align="center" width="180" />
<el-table-column prop="consignee" label="收货人" align="center" width="180" />
<el-table-column prop="goodsStatus" label="货物状态" align="center" width="150" />
<el-table-column prop="createDate" label="创建时间" align="center" width="180" />
<el-table-column
prop="operator"
label="操作"
width="200px"
align="center"
fixed="right"
>
<template #default="scope">
<el-button
type="primary"
size="small"
icon="View"
link
@click="onClickOpenDetail(scope.row)"
>
查看明细
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="footer-pagination">
<div class="btn-export" @click="onClickExport">
<svg-icon icon="Export" className="icon"></svg-icon>
舱单导出
</div>
<div class="pagination">
<Pagination
:pageAble="tableState.pageAble"
:handle-size-change="handleSizeChange"
:handle-current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</div>
</div>
@ -73,13 +116,21 @@
<script lang="ts" setup>
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
import { onMounted, reactive, ref } from 'vue';
import { postSaleShipListAPI } from '@/api/Boat/info';
import { getManifestFileExportAPI, getManifestPageAPI } from '@/api/Manifest';
import { postShipListAPI } from '@/api/Ship';
import Pagination from '@/components/Pagination/Pagination.vue';
import RemoteSelect from '@/components/RemoteSelect/index.vue';
import { useTable } from '@/hooks/useTable';
import { ManifestType } from '@/types/manifest';
const emits = defineEmits(['ManifestID', 'ReturnRouter', 'CurrentNav']);
const subscribeNavList = ref(['舱单信息', '提单明细', '积载图']);
const onClickChangeSubscribeType = (item: string) => {
emits('CurrentNav', item);
};
//
const { getTableList, tableState, searchTable, resetTable, tableChangeCurrent, tableChangeSize } =
useTable({
@ -98,6 +149,7 @@ const searchTableForm = reactive({
billNo: '',
brandId: '',
goodsStatus: '',
deck: '',
});
//
@ -117,12 +169,17 @@ const onClickSearch = async () => {
await searchTable();
tableLoading.value = false;
};
const onClickOpenDeck = async (item: number | string) => {
searchTableForm.deck = item as string;
await onClickSearch();
};
//
const onClickResetForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await resetTable();
formEl.resetFields();
searchTableForm.deck = '';
};
//
@ -167,7 +224,9 @@ const onClickExport = () => {
});
};
const emits = defineEmits(['ManifestID']);
const onClickReturn = () => {
emits('ReturnRouter');
};
//
const onClickOpenDetail = (row: ManifestType) => {
@ -186,42 +245,215 @@ const handleCurrentChange = async (val: number) => {
</script>
<style lang="scss" scoped>
.table {
position: relative;
display: flex;
flex: 1;
flex-direction: column;
.manifest {
width: 100%;
height: 100%;
.header {
box-sizing: border-box;
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: space-between;
padding: 0 16px;
padding: 16px 16px 0;
margin-bottom: 15px;
color: #fff;
.header-left {
display: flex;
.side-icon {
width: 36px;
height: 32px;
font-weight: 400;
line-height: normal;
color: #b4b4b4;
}
.search {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 215px;
margin: 0 20px;
:deep(.el-form-item) {
margin: 0;
}
.search-top {
position: relative;
box-sizing: border-box;
width: 215px;
height: 45px;
margin-bottom: 5px;
border: 3px solid #d7d7d7;
border-radius: 7px;
.card-title {
position: absolute;
top: -14px; /* 使标题悬浮在边框上方 */
left: 20px; /* 根据需要调整水平位置 */
padding: 0 8px; /* 增加一点内边距 */
font-size: 15px;
color: #fff;
background-color: #555;
}
.card-text {
display: flex;
align-items: center;
width: 100%;
height: 100%;
:deep(.el-select__wrapper) {
background-color: transparent;
box-shadow: none;
}
}
}
.search-bottom {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.bottom-one {
width: 90px;
height: 40px;
font-size: 18px;
line-height: 40px; /* 让文字垂直居中 */
text-align: center;
background-color: rgb(22 155 213 / 100%);
border-radius: 5px;
}
.bottom-two {
width: 60px;
height: 36px;
font-size: 18px;
line-height: 40px; /* 让文字垂直居中 */
color: #000;
text-align: center;
background-color: rgb(255 255 204 / 100%);
border-radius: 5px;
}
.bottom-two:hover {
background-color: rgb(255 255 128 / 100%);
}
}
}
.deck {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 380px; /* 6 个按钮宽度 + 间隙 */
height: 90px;
.deck-btn {
width: 60px;
font-size: 18px;
line-height: 36px; /* 让文字垂直居中 */
color: #fff;
text-align: center;
background-color: rgb(0 191 191 / 100%);
border-radius: 5px;
box-shadow: none;
}
.deck-btn-active {
color: #000;
background-color: rgb(255 255 128 / 100%);
}
.deck-btn:hover {
color: #000;
background-color: rgb(255 255 128 / 100%);
}
.deck-btn:nth-child(-n + 6) {
margin-bottom: 10px;
}
}
}
.title {
font-size: 32px;
font-weight: 700;
}
.api-btn {
display: flex;
align-items: center;
justify-content: flex-end;
.btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 50px;
margin-left: 10px;
font-size: 18px;
color: #d7d7d7;
background-color: rgb(51 51 51 / 100%);
border-radius: 75px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
}
.btn-active {
color: #000;
background-color: #f2f2f2;
box-shadow: none;
}
}
}
.footer {
position: relative;
.content {
box-sizing: border-box;
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
padding: 16px;
overflow: hidden;
.footer-util {
.header {
display: flex;
flex-shrink: 0;
justify-content: flex-end;
margin-bottom: 15px;
justify-content: space-between;
padding: 0 16px;
}
.footer-table {
.footer {
position: relative;
flex: 1;
}
.footer-pagination {
box-sizing: border-box;
display: flex;
flex-shrink: 0;
justify-content: flex-end;
width: 100%;
padding-top: 20px;
flex: 1;
flex-direction: column;
padding: 16px;
overflow: hidden;
.footer-util {
display: flex;
flex-shrink: 0;
justify-content: flex-end;
margin-bottom: 15px;
}
.footer-table {
position: relative;
flex: 1;
}
.footer-pagination {
position: relative;
box-sizing: border-box;
display: flex;
align-items: center;
width: 100%;
padding-top: 20px;
.btn-export {
display: flex;
align-items: center;
justify-content: center;
width: 200px;
height: 60px;
font-size: 21px;
color: #fff;
background-color: rgb(0 128 128 / 100%);
border-radius: 5px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
.icon {
width: 32px;
height: 32px;
margin-right: 10px;
color: #80fffe;
}
}
.pagination {
position: absolute;
left: 50%;
// width: 265px;
// height: 50px;
transform: translateX(-50%);
}
}
}
}
}

View File

@ -1,6 +1,7 @@
<template>
<div class="table">
<div class="header">
<div class="home">
<!-- <div class="header">
<svg-icon icon="return" className="side-icon" @click="onClickReturn"></svg-icon>
<div class="title">{{ currentSubscribeNav }}</div>
<div class="api-btn">
<div
@ -13,26 +14,37 @@
{{ item }}
</div>
</div>
</div>
</div> -->
<ManifestTable
v-if="currentSubscribeNav === '舱单信息'"
@ManifestID="onClickOpenDetail"
@ReturnRouter="onClickReturn"
@CurrentNav="onClickChangeSubscribeType"
/>
<!-- <div class="content">
<div class="content">
<ManifestTable v-if="currentSubscribeNav === ''" @ManifestID="onClickOpenDetail" />
<ManifestDetailTable :manifestId="manifestID" v-if="currentSubscribeNav === ''" />
<!-- <ReceiveTable v-if="currentSubscribeNav === ''" /> -->
</div>
</div> -->
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { ManifestType } from '@/types/manifest';
import ManifestDetailTable from './components/ManifestDetailTable.vue';
// import ManifestDetailTable from './components/ManifestDetailTable.vue';
import ManifestTable from './components/ManifestTable.vue';
const router = useRouter();
//
const onClickReturn = () => {
router.back();
};
const manifestID = ref();
//
const subscribeNavList = ref(['舱单信息', '提单明细', '积载图']);
const currentSubscribeNav = ref('舱单信息');
const onClickChangeSubscribeType = async (item: string) => {
if (item === currentSubscribeNav.value) return;
@ -49,51 +61,10 @@ const onClickOpenDetail = (row: ManifestType) => {
</script>
<style lang="scss" scoped>
.table {
.home {
position: relative;
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
height: 100%;
.header {
box-sizing: border-box;
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: space-between;
padding: 16px 16px 0;
margin-bottom: 15px;
color: #fff;
.title {
font-size: 28px;
font-weight: 700;
}
.api-btn {
display: flex;
flex: 1;
align-items: center;
justify-content: flex-end;
.btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 50px;
margin-left: 10px;
font-size: 18px;
color: #d7d7d7;
background-color: rgb(51 51 51 / 100%);
border-radius: 75px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
}
.btn-active {
color: #000;
background-color: #f2f2f2;
box-shadow: none;
}
}
}
.content {
box-sizing: border-box;
flex: 1;

View File

@ -105,7 +105,7 @@ const onClickOpenDialog = async (row: NoticeType) => {
flex: 1;
align-items: flex-end;
justify-content: center;
font-size: 30px;
font-size: 32px;
font-weight: 700;
color: #fff;
}
@ -158,9 +158,9 @@ const onClickOpenDialog = async (row: NoticeType) => {
.content {
position: relative;
box-sizing: border-box;
width: 700px;
height: 100%;
margin-left: 100px;
width: 1100px;
height: 510px;
margin: 0 100px;
font-size: 21px;
font-style: normal;
font-weight: 700;

View File

@ -41,7 +41,7 @@ import ManifestDetailSend from './ManifestDetailSend.vue';
import ManifestSend from './ManifestSend.vue';
// TODO:
const currentActive = ref(2);
const currentActive = ref(1);
const currentTitle = ref(['发布船期信息', '发布舱单信息', '提交舱单明细']);
interface manifestPropsType {

View File

@ -6,16 +6,23 @@
</div>
<!-- 表格 -->
<div class="footer-table">
<el-table v-loading="tableLoading" :data="tableState.tableData" max-height="100%">
<el-table-column prop="enterprise.name" label="企业" align="center" width="150" />
<el-table-column prop="ship.name" label="船舶" align="center" width="150" />
<el-table
v-loading="loadMoreState === 'loading'"
:data="tableData"
max-height="400px"
v-el-table-infinite-scroll="tableDataLoad"
:infinite-scroll-disabled="disabled"
>
<!-- <el-table-column prop="enterprise.name" label="企业" align="center" width="150" /> -->
<el-table-column prop="ship.name" label="船名" align="center" width="150" />
<el-table-column prop="voyage" label="航次" align="center" width="180" />
<el-table-column prop="shipRoute.name" label="航线" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="loadWharf.name" label="装货码头" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="dischargeWharf.name" label="卸货码头" align="center" width="150" />
<el-table-column prop="carNumPlan" label="计划商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column prop="carNumActual" label="实际商品车数量" align="center" width="180" />
<el-table-column prop="spareNumPlan" label="计划件杂货数量" align="center" width="180" />
<el-table-column
prop="spareNumActual"
label="实际件杂货数量"
@ -35,10 +42,7 @@
width="200"
/>
<el-table-column prop="tradeType" label="贸易类型" align="center" width="150" />
<el-table-column prop="loadPort.name" label="装货港口" align="center" width="150" />
<el-table-column prop="dischargePort.name" label="卸货港口" align="center" width="150" />
<el-table-column prop="shipStatus" label="当前状态" align="center" width="150" />
<el-table-column prop="createDate" label="创建时间" align="center" width="180" />
<el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
<template #default="scope">
<el-button
@ -67,33 +71,52 @@
<script lang="ts" setup>
import dayjs from 'dayjs';
import { onMounted, ref } from 'vue';
import { computed, ref } from 'vue';
import { getSailScheduleGetAPI, getSailSchedulePageAPI } from '@/api/Boat/info';
import { useTable } from '@/hooks/useTable';
import { PageRowsResult } from '@/types';
import { BoatInfoType } from '@/types/boatInfo';
const handleTableData = (data: PageRowsResult<BoatInfoType>) => {
const { records } = data;
for (const item of records) {
item.departureDatePlan = dayjs(item.departureDatePlan).format('YYYY-MM-DD HH:mm:ss');
item.departureDateActual = dayjs(item.departureDateActual).format('YYYY-MM-DD HH:mm:ss');
const handleTableData = (data: BoatInfoType[]) => {
for (const item of data) {
item.departureDatePlan = dayjs(item.departureDatePlan).format('YYYY-MM-DD HH:mm');
item.departureDateActual = dayjs(item.departureDateActual).format('YYYY-MM-DD HH:mm');
}
return data;
};
//
const { getTableList, tableState } = useTable({
api: getSailSchedulePageAPI,
dataCallBack: handleTableData,
});
//
const tableData = ref<BoatInfoType[]>([]);
onMounted(async () => {
await getTableList();
});
// -
const currentPage = ref(1);
//
const tableLoading = ref(false);
//
const loadMoreState = ref('loading');
const disabled = computed(() => loadMoreState.value === 'finished');
//
const getTableDataList = async () => {
const { data } = await getSailSchedulePageAPI({
page: currentPage.value,
rows: 10,
});
//
const dataRecords = handleTableData(data.records);
tableData.value = [...tableData.value, ...dataRecords];
currentPage.value++;
//
if (currentPage.value > Number(data.pages) || data.records.length < 10) {
loadMoreState.value = 'finished';
} else {
loadMoreState.value = 'load';
}
};
const tableDataLoad = async () => {
await getTableDataList();
};
const emits = defineEmits(['sendInfo']);

View File

@ -26,7 +26,7 @@
<div class="right-title">操作权限</div>
<div class="content">
<div class="content-title" v-if="currentUser.name">{{ currentUser.name }}</div>
<el-checkbox-group v-model="currentUser.perms">
<el-checkbox-group v-model="currentUser.perms" size="large" fill="#027DB4">
<div class="content-checkbox">
<div class="checkbox-title">查看船货信息</div>
<div class="checkbox">
@ -50,9 +50,7 @@
</div>
</el-checkbox-group>
<div class="content-btn">
<el-button type="primary" @click="onClickSubmit"> </el-button>
</div>
<div class="content-btn" @click="onClickSubmit"></div>
</div>
</div>
</div>
@ -228,8 +226,35 @@ const employeeListLoad = async () => {
}
.checkbox {
flex: 1;
:deep(.el-checkbox) {
--el-checkbox-checked-text-color: #027db4;
--el-checkbox-checked-bg-color: #027db4;
}
:deep(.el-checkbox.el-checkbox--large .el-checkbox__inner) {
width: 32px;
height: 32px;
}
:deep(.el-checkbox__inner::after) {
top: 6px;
left: 14px;
height: 18px;
}
:deep(.el-checkbox.el-checkbox--large .el-checkbox__label) {
font-size: 18px;
}
}
}
.content-btn {
width: 140px;
height: 40px;
font-size: 18px;
line-height: 40px;
color: #fff;
text-align: center;
background-color: rgb(22 155 213 / 100%);
border-radius: 5px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
}
}
}
}

View File

@ -1,6 +1,9 @@
<template>
<div class="table">
<div class="employeeTable">
<div class="footer">
<div class="footer-util">
<div class="title">本单位用户</div>
</div>
<!-- 表格 -->
<div class="footer-table">
<el-table v-loading="tableLoading" :data="tableState.tableData" max-height="460">
@ -19,7 +22,6 @@
</template>
</el-table-column>
<el-table-column prop="phone" label="联系电话" align="center" width="120" />
<el-table-column prop="createDate" label="创建时间" align="center" width="180" />
<el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
<template #default="scope">
<el-button
@ -125,7 +127,7 @@ const onClickDel = (row: EmployeeType) => {
</script>
<style lang="scss" scoped>
.table {
.employeeTable {
position: relative;
display: flex;
flex: 1;
@ -138,12 +140,32 @@ const onClickDel = (row: EmployeeType) => {
}
.footer {
position: relative;
box-sizing: border-box;
display: flex;
flex: 1;
flex-direction: column;
padding: 16px;
overflow: hidden;
.footer-util {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 10px;
.title {
font-size: 21px;
font-style: normal;
font-weight: 700;
color: #fff;
}
}
.footer-table {
:deep(.el-table__row) {
color: #000 !important;
background-color: #fff !important;
}
:deep(.el-table__body tr.hover-row > td.el-table__cell) {
color: #000;
background-color: #fff;
}
}
.footer-pagination {
display: flex;
align-items: center;

View File

@ -9,6 +9,7 @@
<div class="list-item">堆存能力{{ wharfInfo?.storageCapacity }}</div>
<div class="list-item">接卸能力{{ wharfInfo?.handlingCapacity }}</div>
<div class="list-item">简介{{ wharfInfo?.intro }}</div>
<div class="list-btn">编辑</div>
</div>
<div class="content">
@ -66,26 +67,40 @@ onMounted(async () => {
width: 100%;
height: 100%;
.list-wrap {
width: 30%;
width: 527px;
height: 100%;
.title {
width: 100%;
font-size: 26px;
font-weight: 700;
line-height: 40px;
}
.list-item {
width: 100%;
font-size: 20px;
line-height: 40px;
}
.list-btn {
width: 140px;
height: 40px;
margin-top: 40px;
font-size: 18px;
line-height: 40px;
color: #fff;
text-align: center;
cursor: pointer;
background-color: rgb(22 155 213 / 100%);
border-radius: 5px;
}
}
.content {
box-sizing: border-box;
flex: 1;
height: 100%;
margin-left: 20px;
width: 1000px;
height: 585px;
margin-left: 40px;
img {
width: 100%;
height: calc(100% - 40px);
height: 100%;
}
}
}

View File

@ -2,16 +2,18 @@
<div class="table">
<div class="header">
<svg-icon icon="return" className="side-icon" @click="onClickReturn"></svg-icon>
<div class="title">用户设置</div>
<div class="api-btn">
<div
class="btn"
:class="{ 'btn-active': currentNav === item }"
v-for="item in helpDocsList"
:key="item"
@click="onClickChangeNav(item)"
>
{{ item }}
<div class="header-right">
<div class="title">用户设置</div>
<div class="api-btn">
<div
class="btn"
:class="{ 'btn-active': currentNav === item }"
v-for="item in helpDocsList"
:key="item"
@click="onClickChangeNav(item)"
>
{{ item }}
</div>
</div>
</div>
</div>
@ -50,11 +52,13 @@ const onClickChangeNav = (item: string) => {
<style lang="scss" scoped>
.table {
position: relative;
box-sizing: border-box;
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
height: 100%;
padding-right: 40px;
.header {
box-sizing: border-box;
display: flex;
@ -71,31 +75,37 @@ const onClickChangeNav = (item: string) => {
line-height: normal;
color: #b4b4b4;
}
.title {
font-size: 28px;
font-weight: 700;
}
.api-btn {
.header-right {
display: flex;
align-items: center;
justify-content: flex-end;
.btn {
.title {
box-sizing: border-box;
padding-right: 120px;
font-size: 32px;
font-weight: 700;
}
.api-btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 50px;
margin-left: 10px;
font-size: 18px;
color: #d7d7d7;
background-color: rgb(51 51 51 / 100%);
border-radius: 75px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
}
.btn-active {
color: #000;
background-color: #f2f2f2;
box-shadow: none;
justify-content: flex-end;
.btn {
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 50px;
margin-left: 10px;
font-size: 18px;
color: #d7d7d7;
background-color: rgb(51 51 51 / 100%);
border-radius: 75px;
box-shadow: 5px 5px 5px rgb(0 0 0 / 34.9%);
}
.btn-active {
color: #000;
background-color: #f2f2f2;
box-shadow: none;
}
}
}
}

View File

@ -165,7 +165,7 @@ const onClickLogout = async () => {
.password {
width: 140px;
height: 53px;
margin-top: 20px;
margin-top: 200px;
font-size: 21px;
line-height: 53px;
color: #fff;
@ -178,7 +178,7 @@ const onClickLogout = async () => {
}
.logout {
position: absolute;
right: 20px;
right: 60px;
bottom: 20px;
width: 140px;
height: 53px;